<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<script type="text/javascript">
var version = {major: 2, minor: 0, revision: 10, date: new Date("Apr 19, 2006"), extensions: {}};
</script>
<!--
TiddlyWiki 2.0.10 by Jeremy Ruston, (jeremy [at] osmosoft [dot] com)

Copyright (c) Osmosoft Limited 2004-2006

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.

Neither the name of the Osmosoft Limited nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
-->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<!--PRE-HEAD-START-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'>
<!--PRE-HEAD-END-->
<title> Google Widget Toolkit - User library -  - Gem based modifications </title>
<script type="text/javascript">

// ---------------------------------------------------------------------------------
// Configuration repository
// ---------------------------------------------------------------------------------

// Miscellaneous options
var config = {
	numRssItems: 20, // Number of items in the RSS feed
	animFast: 0.12, // Speed for animations (lower == slower)
	animSlow: 0.01, // Speed for EasterEgg animations
	cascadeFast: 20, // Speed for cascade animations (higher == slower)
	cascadeSlow: 60, // Speed for EasterEgg cascade animations
	cascadeDepth: 5 // Depth of cascade animation
	};

// Options that can be set in the options panel and/or cookies
config.options = {
	chkRegExpSearch: false,
	chkCaseSensitiveSearch: false,
	chkAnimate: true,
	txtUserName: "YourName",
	chkSaveBackups: true,
	chkAutoSave: false,
	chkGenerateAnRssFeed: false,
	chkSaveEmptyTemplate: false,
	chkOpenInNewWindow: true,
	chkToggleLinks: false,
	chkHttpReadOnly: true,
	chkForceMinorUpdate: false,
	chkConfirmDelete: true,
	txtBackupFolder: "",
	txtMainTab: "tabTimeline",
	txtMoreTab: "moreTabAll",
	txtMaxEditRows: "30"
	};

// List of notification functions to be called when certain tiddlers are changed or deleted
config.notifyTiddlers = [
	{name: "StyleSheetLayout", notify: refreshStyles},
	{name: "StyleSheetColors", notify: refreshStyles},
	{name: "StyleSheet", notify: refreshStyles},
	{name: "StyleSheetPrint", notify: refreshStyles},
	{name: "PageTemplate", notify: refreshPageTemplate},
	{name: null, notify: refreshDisplay}
	];

// Default tiddler templates
var DEFAULT_VIEW_TEMPLATE = 1;
var DEFAULT_EDIT_TEMPLATE = 2;
config.tiddlerTemplates = {
	1: "ViewTemplate",
	2: "EditTemplate"
	};

// Messages
config.messages = {
	customConfigError: "Error in systemConfig tiddler '%1' - %0",
	savedSnapshotError: "It appears that this TiddlyWiki has been incorrectly saved. Please see http://www.tiddlywiki.com/#DownloadSoftware for details",
	subtitleUnknown: "(unknown)",
	undefinedTiddlerToolTip: "The tiddler '%0' doesn't yet exist",
	shadowedTiddlerToolTip: "The tiddler '%0' doesn't yet exist, but has a pre-defined shadow value",
	tiddlerLinkTooltip: "%0 - %1, %2",
	externalLinkTooltip: "External link to %0",
	noTags: "There are no tagged tiddlers",
	notFileUrlError: "You need to save this TiddlyWiki to a file before you can save changes",
	cantSaveError: "It's not possible to save changes. This could be because your browser doesn't support saving (instead, use FireFox if you can), or because the pathname to your TiddlyWiki file contains illegal characters",
	invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
	backupSaved: "Backup saved",
	backupFailed: "Failed to save backup file",
	rssSaved: "RSS feed saved",
	rssFailed: "Failed to save RSS feed file",
	emptySaved: "Empty template saved",
	emptyFailed: "Failed to save empty template file",
	mainSaved: "Main TiddlyWiki file saved",
	mainFailed: "Failed to save main TiddlyWiki file. Your changes have not been saved",
	macroError: "Error in macro <<%0>>",
	macroErrorDetails: "Error while executing macro <<%0>>:\n%1",
	missingMacro: "No such macro",
	overwriteWarning: "A tiddler named '%0' already exists. Choose OK to overwrite it",
	unsavedChangesWarning: "WARNING! There are unsaved changes in TiddlyWiki\n\nChoose OK to save\nChoose CANCEL to discard",
	confirmExit: "--------------------------------\n\nThere are unsaved changes in TiddlyWiki. If you continue you will lose those changes\n\n--------------------------------",
	saveInstructions: "SaveChanges",
	messageClose: {text: "close", tooltip: "close this message area"},
	dates: {
		months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November","December"],
		days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
		}
	};

// More messages (rather a legacy layout that shouldn't really be like this)
config.views = {
	wikified: {
		tag: {labelNoTags: "no tags", labelTags: "tags: ", openTag: "Open tag '%0'", tooltip: "Show tiddlers tagged with '%0'", openAllText: "Open all", openAllTooltip: "Open all of these tiddlers", popupNone: "No other tiddlers tagged with '%0'"},
		defaultText: "The tiddler '%0' doesn't yet exist. Double-click to create it",
		defaultModifier: "(missing)",
		shadowModifier: "(shadow)"
		},
	editor: {
		tagPrompt: "Type tags separated with spaces, [[use double square brackets]] if necessary, or add existing",
		tagChooser: {text: "tags", tooltip: "Choose existing tags to add to this tiddler", popupNone: "There are no tags defined", tagTooltip: "Add the tag '%0'"},
		defaultText: "Type the text for '%0'"
		}
	};

// Macros; each has a 'handler' member that is inserted later
config.macros = {
	today: {},
	version: {},
	search: {label: "search", prompt: "Search this TiddlyWiki", sizeTextbox: 15, accessKey: "F", successMsg: "%0 tiddlers found matching %1", failureMsg: "No tiddlers found matching %0"},
	tiddler: {},
	tag: {},
	tags: {},
	tagging: {label: "tagging:", labelNotTag: "not tagging", tooltip: "List of tiddlers tagged with '%0'"},
	timeline: {dateFormat: "DD MMM YYYY"},
	allTags: {tooltip: "Show tiddlers tagged with '%0'", noTags: "There are no tagged tiddlers"},
	list: {
		all: {prompt: "All tiddlers in alphabetical order"},
		missing: {prompt: "Tiddlers that have links to them but are not defined"},
		orphans: {prompt: "Tiddlers that are not linked to from any other tiddlers"},
		shadowed: {prompt: "Tiddlers shadowed with default contents"}
		},
	closeAll: {label: "close all", prompt: "Close all displayed tiddlers (except any that are being edited)"},
	permaview: {label: "permaview", prompt: "Link to an URL that retrieves all the currently displayed tiddlers"},
	saveChanges: {label: "save changes", prompt: "Save all tiddlers to create a new TiddlyWiki", accessKey: "S"},
	slider: {},
	option: {},
	newTiddler: {label: "new tiddler", prompt: "Create a new tiddler", title: "New Tiddler", accessKey: "N"},
	newJournal: {label: "new journal", prompt: "Create a new tiddler from the current date and time", accessKey: "J"},
	sparkline: {},
	tabs: {},
	gradient: {},
	message: {},
	view: {},
	edit: {},
	tagChooser: {},
	toolbar: {},
	br: {}
	};

// Commands supported by the toolbar macro
config.commands = {
	closeTiddler: {text: "close", tooltip: "Close this tiddler"},
	closeOthers: {text: "close others", tooltip: "Close all other tiddlers"},
	editTiddler: {text: "edit", tooltip: "Edit this tiddler", readOnlyText: "view", readOnlyTooltip: "View the source of this tiddler"},
	saveTiddler: {text: "done", tooltip: "Save changes to this tiddler", hideReadOnly: true},
	cancelTiddler: {text: "cancel", tooltip: "Undo changes to this tiddler", warning: "Are you sure you want to abandon your changes to '%0'?", readOnlyText: "done", readOnlyTooltip: "View this tiddler normally"},
	deleteTiddler: {text: "delete", tooltip: "Delete this tiddler", warning: "Are you sure you want to delete '%0'?", hideReadOnly: true},
	permalink: {text: "permalink", tooltip: "Permalink for this tiddler"},
	references: {text: "references", tooltip: "Show tiddlers that link to this one", popupNone: "No references"},
	jump: {text: "jump", tooltip: "Jump to another open tiddler"}
	};

// Browser detection... In a very few places, there's nothing else for it but to
// know what browser we're using.
config.userAgent = navigator.userAgent.toLowerCase();
config.browser = {
	isIE: config.userAgent.indexOf("msie") != -1 && config.userAgent.indexOf("opera") == -1,
	isSafari: config.userAgent.indexOf("applewebkit") != -1,
	isBadSafari: !((new RegExp("[\u0150\u0170]","g")).test("\u0150")),
	firefoxDate: /Gecko\/(\d{8})/i.exec(config.userAgent),
	isLinux: config.userAgent.indexOf("linux") != -1,
	isUnix: config.userAgent.indexOf("x11") != -1,
	isMac: config.userAgent.indexOf("mac") != -1,
	isWindows: config.userAgent.indexOf("win") != -1
	};

// Basic regular expressions
config.textPrimitives = {
	upperLetter: "[A-Z\u00c0-\u00de\u0150\u0170]",
	lowerLetter: "[a-z\u00df-\u00ff_0-9\\-\u0151\u0171]",
	anyLetter: "[A-Za-z\u00c0-\u00de\u00df-\u00ff_0-9\\-\u0150\u0170\u0151\u0171]"
	};
if(config.browser.isBadSafari)
	config.textPrimitives = {
		upperLetter: "[A-Z\u00c0-\u00de]",
		lowerLetter: "[a-z\u00df-\u00ff_0-9\\-]",
		anyLetter: "[A-Za-z\u00c0-\u00de\u00df-\u00ff_0-9\\-]"
		}
config.textPrimitives.anyDigit = "[0-9]";
config.textPrimitives.anyNumberChar = "[0-9\\.E]";
config.textPrimitives.urlPattern = "(?:file|http|https|mailto|ftp):[^\\s'\"]+(?:/|\\b)";
config.textPrimitives.unWikiLink = "~";
config.textPrimitives.wikiLink = "(?:" + config.textPrimitives.unWikiLink + "{0,1})(?:(?:" + config.textPrimitives.upperLetter + "+" +
												  config.textPrimitives.lowerLetter + "+" +
												  config.textPrimitives.upperLetter +
												  config.textPrimitives.anyLetter + "*)|(?:" +
												  config.textPrimitives.upperLetter + "{2,}" +
												  config.textPrimitives.lowerLetter + "+))";

// ---------------------------------------------------------------------------------
// Shadow tiddlers for emergencies
// ---------------------------------------------------------------------------------

config.shadowTiddlers = {
	DefaultTiddlers: "GettingStarted",
	MainMenu: "GettingStarted",
	SiteTitle: "My TiddlyWiki",
	SiteSubtitle: "a reusable non-linear personal web notebook",
	SiteUrl: "http://www.tiddlywiki.com/",
	GettingStarted: "To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:\n* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)\n* MainMenu: The menu (usually on the left)\n* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened\nYou'll also need to enter your username for signing your edits: <<option txtUserName>>",
	SideBarOptions: "<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal 'DD MMM YYYY'>><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel 'options' 'Change TiddlyWiki advanced options'>>",
	OptionsPanel: "These InterfaceOptions for customising TiddlyWiki are saved in your browser\n\nYour username for signing your edits. Write it as a WikiWord (eg JoeBloggs)\n\n<<option txtUserName>>\n<<option chkSaveBackups>> SaveBackups\n<<option chkAutoSave>> AutoSave\n<<option chkRegExpSearch>> RegExpSearch\n<<option chkCaseSensitiveSearch>> CaseSensitiveSearch\n<<option chkAnimate>> EnableAnimations\n\nSee AdvancedOptions",
	AdvancedOptions: "<<option chkGenerateAnRssFeed>> GenerateAnRssFeed\n<<option chkOpenInNewWindow>> OpenLinksInNewWindow\n<<option chkSaveEmptyTemplate>> SaveEmptyTemplate\n<<option chkToggleLinks>> Clicking on links to tiddlers that are already open causes them to close\n^^(override with Control or other modifier key)^^\n<<option chkHttpReadOnly>> HideEditingFeatures when viewed over HTTP\n<<option chkForceMinorUpdate>> Treat edits as MinorChanges by preserving date and time\n^^(override with Shift key when clicking 'done' or by pressing Ctrl-Shift-Enter^^\n<<option chkConfirmDelete>> ConfirmBeforeDeleting\nMaximum number of lines in a tiddler edit box: <<option txtMaxEditRows>>\nFolder name for backup files: <<option txtBackupFolder>>\n",
	SideBarTabs: "<<tabs txtMainTab Timeline Timeline TabTimeline All 'All tiddlers' TabAll Tags 'All tags' TabTags More 'More lists' TabMore>>",
	TabTimeline: "<<timeline>>",
	TabAll: "<<list all>>",
	TabTags: "<<allTags>>",
	TabMore: "<<tabs txtMoreTab Missing 'Missing tiddlers' TabMoreMissing Orphans 'Orphaned tiddlers' TabMoreOrphans Shadowed 'Shadowed tiddlers' TabMoreShadowed>>",
	TabMoreMissing: "<<list missing>>",
	TabMoreOrphans: "<<list orphans>>",
	TabMoreShadowed: "<<list shadowed>>",
	StyleSheet: "/***\nPlace your custom CSS here\n***/\n/*{{{*/\n\n/*}}}*/\n",
	StyleSheetColors: "/***\n!Colors Used\n*@@bgcolor(#8cf): #8cf - Background blue@@\n*@@bgcolor(#18f): #18f - Top blue@@\n*@@bgcolor(#04b): #04b - Mid blue@@\n*@@bgcolor(#014):color(#fff): #014 - Bottom blue@@\n*@@bgcolor(#ffc): #ffc - Bright yellow@@\n*@@bgcolor(#fe8): #fe8 - Highlight yellow@@\n*@@bgcolor(#db4): #db4 - Background yellow@@\n*@@bgcolor(#841): #841 - Border yellow@@\n*@@bgcolor(#703):color(#fff): #703 - Title red@@\n*@@bgcolor(#866): #866 - Subtitle grey@@\n!Generic Rules /%==============================================%/\n***/\n/*{{{*/\nbody {\n	background: #fff;\n	color: #000;\n}\n\na{\n	color: #04b;\n}\n\na:hover{\n	background: #04b;\n	color: #fff;\n}\n\na img{\n	border: 0;\n}\n\nh1,h2,h3,h4,h5 {\n	color: #703;\n	background: #8cf;\n}\n\n.button {\n	color: #014;\n	border: 1px solid #fff;\n}\n\n.button:hover {\n	color: #014;\n	background: #fe8;\n	border-color: #db4;\n}\n\n.button:active {\n	color: #fff;\n	background: #db4;\n	border: 1px solid #841;\n}\n\n/*}}}*/\n/***\n!Header /%==================================================%/\n***/\n/*{{{*/\n.header {\n	background: #04b;\n}\n\n.headerShadow {\n	color: #000;\n}\n\n.headerShadow a {\n	font-weight: normal;\n	color: #000;\n}\n\n.headerForeground {\n	color: #fff;\n}\n\n.headerForeground a {\n	font-weight: normal;\n	color: #8cf;\n}\n\n/*}}}*/\n/***\n!General tabs /%=================================================%/\n***/\n/*{{{*/\n\n.tabSelected{\n	color: #014;\n	background: #eee;\n	border-left: 1px solid #ccc;\n	border-top: 1px solid #ccc;\n	border-right: 1px solid #ccc;\n}\n\n.tabUnselected {\n	color: #fff;\n	background: #999;\n}\n\n.tabContents {\n	color: #014;\n	background: #eee;\n	border: 1px solid #ccc;\n}\n\n.tabContents .button {\n	 border: 0;}\n\n/*}}}*/\n/***\n!Sidebar options /%=================================================%/\n~TiddlyLinks and buttons are treated identically in the sidebar and slider panel\n***/\n/*{{{*/\n#sidebar {\n}\n\n#sidebarOptions input {\n	border: 1px solid #04b;\n}\n\n#sidebarOptions .sliderPanel {\n	background: #8cf;\n}\n\n#sidebarOptions .sliderPanel a {\n	border: none;\n	color: #04b;\n}\n\n#sidebarOptions .sliderPanel a:hover {\n	color: #fff;\n	background: #04b;\n}\n\n#sidebarOptions .sliderPanel a:active {\n	color: #04b;\n	background: #fff;\n}\n/*}}}*/\n/***\n!Message Area /%=================================================%/\n***/\n/*{{{*/\n#messageArea {\n	border: 1px solid #841;\n	background: #db4;\n	color: #014;\n}\n\n#messageArea .button {\n	padding: 0.2em 0.2em 0.2em 0.2em;\n	color: #014;\n	background: #fff;\n}\n\n/*}}}*/\n/***\n!Popup /%=================================================%/\n***/\n/*{{{*/\n.popup {\n	background: #18f;\n	border: 1px solid #04b;\n}\n\n.popup hr {\n	color: #014;\n	background: #014;\n	border-bottom: 1px;\n}\n\n.popup li.disabled {\n	color: #04b;\n}\n\n.popup li a, .popup li a:visited {\n	color: #eee;\n	border: none;\n}\n\n.popup li a:hover {\n	background: #014;\n	color: #fff;\n	border: none;\n}\n/*}}}*/\n/***\n!Tiddler Display /%=================================================%/\n***/\n/*{{{*/\n.tiddler .defaultCommand {\n font-weight: bold;\n}\n\n.shadow .title {\n	color: #866;\n}\n\n.title {\n	color: #703;\n}\n\n.subtitle {\n	color: #866;\n}\n\n.toolbar {\n	color: #04b;\n}\n\n.tagging, .tagged {\n	border: 1px solid #eee;\n	background-color: #eee;\n}\n\n.selected .tagging, .selected .tagged {\n	background-color: #ddd;\n	border: 1px solid #bbb;\n}\n\n.tagging .listTitle, .tagged .listTitle {\n	color: #014;\n}\n\n.tagging .button, .tagged .button {\n		border: none;\n}\n\n.footer {\n	color: #ddd;\n}\n\n.selected .footer {\n	color: #888;\n}\n\n.sparkline {\n	background: #8cf;\n	border: 0;\n}\n\n.sparktick {\n	background: #014;\n}\n\n.errorButton {\n	color: #ff0;\n	background: #f00;\n}\n\n.cascade {\n	background: #eef;\n	color: #aac;\n	border: 1px solid #aac;\n}\n\n.imageLink, #displayArea .imageLink {\n	background: transparent;\n}\n\n/*}}}*/\n/***\n''The viewer is where the tiddler content is displayed'' /%------------------------------------------------%/\n***/\n/*{{{*/\n\n.viewer .listTitle {list-style-type: none; margin-left: -2em;}\n\n.viewer .button {\n	border: 1px solid #db4;\n}\n\n.viewer blockquote {\n	border-left: 3px solid #666;\n}\n\n.viewer table {\n	border: 2px solid #333;\n}\n\n.viewer th, thead td {\n	background: #db4;\n	border: 1px solid #666;\n	color: #fff;\n}\n\n.viewer td, .viewer tr {\n	border: 1px solid #666;\n}\n\n.viewer pre {\n	border: 1px solid #fe8;\n	background: #ffc;\n}\n\n.viewer code {\n	color: #703;\n}\n\n.viewer hr {\n	border: 0;\n	border-top: dashed 1px #666;\n	color: #666;\n}\n\n.highlight, .marked {\n	background: #fe8;\n}\n/*}}}*/\n/***\n''The editor replaces the viewer in the tiddler'' /%------------------------------------------------%/\n***/\n/*{{{*/\n.editor input {\n	border: 1px solid #04b;\n}\n\n.editor textarea {\n	border: 1px solid #04b;\n	width: 100%;\n}\n\n.editorFooter {\n	color: #aaa;\n}\n\n/*}}}*/",
	StyleSheetLayout: "/***\n!Sections in this Tiddler:\n*Generic rules\n**Links styles\n**Link Exceptions\n*Header\n*Main menu\n*Sidebar\n**Sidebar options\n**Sidebar tabs\n*Message area\n*Popup\n*Tabs\n*Tiddler display\n**Viewer\n**Editor\n*Misc. rules\n!Generic Rules /%==============================================%/\n***/\n/*{{{*/\nbody {\n	font-size: .75em;\n	font-family: arial,helvetica;\n	position: relative;\n	margin: 0;\n	padding: 0;\n}\n\nh1,h2,h3,h4,h5 {\n	font-weight: bold;\n	text-decoration: none;\n	padding-left: 0.4em;\n}\n\nh1 {font-size: 1.35em;}\nh2 {font-size: 1.25em;}\nh3 {font-size: 1.1em;}\nh4 {font-size: 1em;}\nh5 {font-size: .9em;}\n\nhr {\n	height: 1px;\n}\n\na{\n	text-decoration: none;\n}\n\nol { list-style-type: decimal }\nol ol { list-style-type: lower-alpha }\nol ol ol { list-style-type: lower-roman }\nol ol ol ol { list-style-type: decimal }\nol ol ol ol ol { list-style-type: lower-alpha }\nol ol ol ol ol ol { list-style-type: lower-roman }\nol ol ol ol ol ol ol { list-style-type: decimal }\n/*}}}*/\n/***\n''General Link Styles'' /%-----------------------------------------------------------------------------%/\n***/\n/*{{{*/\n.externalLink {\n	text-decoration: underline;\n}\n\n.tiddlyLinkExisting {\n	font-weight: bold;\n}\n\n.tiddlyLinkNonExisting {\n	font-style: italic;\n}\n\n/* the 'a' is required for IE, otherwise it renders the whole tiddler a bold */\na.tiddlyLinkNonExisting.shadow {\n	font-weight: bold;\n}\n/*}}}*/\n/***\n''Exceptions to common link styles'' /%------------------------------------------------------------------%/\n***/\n/*{{{*/\n\n#mainMenu .tiddlyLinkExisting, \n#mainMenu .tiddlyLinkNonExisting,\n#sidebarTabs .tiddlyLinkExisting,\n#sidebarTabs .tiddlyLinkNonExisting{\n font-weight: normal;\n font-style: normal;\n}\n\n/*}}}*/\n/***\n!Header /%==================================================%/\n***/\n/*{{{*/\n\n.header {\n		position: relative;\n}\n\n.header a:hover {\n	background: transparent;\n}\n\n.headerShadow {\n	position: relative;\n	padding: 4.5em 0em 1em 1em;\n	left: -1px;\n	top: -1px;\n}\n\n.headerForeground {\n	position: absolute;\n	padding: 4.5em 0em 1em 1em;\n	left: 0px;\n	top: 0px;\n}\n\n.siteTitle {\n	font-size: 3em;\n}\n\n.siteSubtitle {\n	font-size: 1.2em;\n}\n\n/*}}}*/\n/***\n!Main menu /%==================================================%/\n***/\n/*{{{*/\n#mainMenu {\n	position: absolute;\n	left: 0;\n	width: 10em;\n	text-align: right;\n	line-height: 1.6em;\n	padding: 1.5em 0.5em 0.5em 0.5em;\n	font-size: 1.1em;\n}\n\n/*}}}*/\n/***\n!Sidebar rules /%==================================================%/\n***/\n/*{{{*/\n#sidebar {\n	position: absolute;\n	right: 3px;\n	width: 16em;\n	font-size: .9em;\n}\n/*}}}*/\n/***\n''Sidebar options'' /%----------------------------------------------------------------------------------%/\n***/\n/*{{{*/\n#sidebarOptions {\n	padding-top: 0.3em;\n}\n\n#sidebarOptions a {\n	margin: 0em 0.2em;\n	padding: 0.2em 0.3em;\n	display: block;\n}\n\n#sidebarOptions input {\n	margin: 0.4em 0.5em;\n}\n\n#sidebarOptions .sliderPanel {\n	margin-left: 1em;\n	padding: 0.5em;\n	font-size: .85em;\n}\n\n#sidebarOptions .sliderPanel a {\n	font-weight: bold;\n	display: inline;\n	padding: 0;\n}\n\n#sidebarOptions .sliderPanel input {\n	margin: 0 0 .3em 0;\n}\n/*}}}*/\n/***\n''Sidebar tabs'' /%-------------------------------------------------------------------------------------%/\n***/\n/*{{{*/\n\n#sidebarTabs .tabContents {\n	width: 15em;\n	overflow: hidden;\n}\n\n/*}}}*/\n/***\n!Message area /%==================================================%/\n***/\n/*{{{*/\n#messageArea {\nposition:absolute; top:0; right:0; margin: 0.5em; padding: 0.5em;\n}\n\n*[id='messageArea'] {\nposition:fixed !important; z-index:99;}\n\n.messageToolbar {\ndisplay: block;\ntext-align: right;\n}\n\n#messageArea a{\n	text-decoration: underline;\n}\n/*}}}*/\n/***\n!Popup /%==================================================%/\n***/\n/*{{{*/\n.popup {\n	font-size: .9em;\n	padding: 0.2em;\n	list-style: none;\n	margin: 0;\n}\n\n.popup hr {\n	display: block;\n	height: 1px;\n	width: auto;\n	padding: 0;\n	margin: 0.2em 0em;\n}\n\n.popup li.disabled {\n	padding: 0.2em;\n}\n\n.popup li a{\n	display: block;\n	padding: 0.2em;\n}\n/*}}}*/\n/***\n!Tabs /%==================================================%/\n***/\n/*{{{*/\n.tabset {\n	padding: 1em 0em 0em 0.5em;\n}\n\n.tab {\n	margin: 0em 0em 0em 0.25em;\n	padding: 2px;\n}\n\n.tabContents {\n	padding: 0.5em;\n}\n\n.tabContents ul, .tabContents ol {\n	margin: 0;\n	padding: 0;\n}\n\n.txtMainTab .tabContents li {\n	list-style: none;\n}\n\n.tabContents li.listLink {\n	 margin-left: .75em;\n}\n/*}}}*/\n/***\n!Tiddler display rules /%==================================================%/\n***/\n/*{{{*/\n#displayArea {\n	margin: 1em 17em 0em 14em;\n}\n\n\n.toolbar {\n	text-align: right;\n	font-size: .9em;\n	visibility: hidden;\n}\n\n.selected .toolbar {\n	visibility: visible;\n}\n\n.tiddler {\n	padding: 1em 1em 0em 1em;\n}\n\n.missing .viewer,.missing .title {\n	font-style: italic;\n}\n\n.title {\n	font-size: 1.6em;\n	font-weight: bold;\n}\n\n.missing .subtitle {\n display: none;\n}\n\n.subtitle {\n	font-size: 1.1em;\n}\n\n/* I'm not a fan of how button looks in tiddlers... */\n.tiddler .button {\n	padding: 0.2em 0.4em;\n}\n\n.tagging {\nmargin: 0.5em 0.5em 0.5em 0;\nfloat: left;\ndisplay: none;\n}\n\n.isTag .tagging {\ndisplay: block;\n}\n\n.tagged {\nmargin: 0.5em;\nfloat: right;\n}\n\n.tagging, .tagged {\nfont-size: 0.9em;\npadding: 0.25em;\n}\n\n.tagging ul, .tagged ul {\nlist-style: none;margin: 0.25em;\npadding: 0;\n}\n\n.tagClear {\nclear: both;\n}\n\n.footer {\n	font-size: .9em;\n}\n\n.footer li {\ndisplay: inline;\n}\n/***\n''The viewer is where the tiddler content is displayed'' /%------------------------------------------------%/\n***/\n/*{{{*/\n* html .viewer pre {\n	width: 99%;\n	padding: 0 0 1em 0;\n}\n\n.viewer {\n	line-height: 1.4em;\n	padding-top: 0.5em;\n}\n\n.viewer .button {\n	margin: 0em 0.25em;\n	padding: 0em 0.25em;\n}\n\n.viewer blockquote {\n	line-height: 1.5em;\n	padding-left: 0.8em;\n	margin-left: 2.5em;\n}\n\n.viewer ul, .viewer ol{\n	margin-left: 0.5em;\n	padding-left: 1.5em;\n}\n\n.viewer table {\n	border-collapse: collapse;\n	margin: 0.8em 1.0em;\n}\n\n.viewer th, .viewer td, .viewer tr,.viewer caption{\n	padding: 3px;\n}\n\n.viewer pre {\n	padding: 0.5em;\n	margin-left: 0.5em;\n	font-size: 1.2em;\n	line-height: 1.4em;\n	overflow: auto;\n}\n\n.viewer code {\n	font-size: 1.2em;\n	line-height: 1.4em;\n}\n/*}}}*/\n/***\n''The editor replaces the viewer in the tiddler'' /%------------------------------------------------%/\n***/\n/*{{{*/\n.editor {\nfont-size: 1.1em;\n}\n\n.editor input, .editor textarea {\n	display: block;\n	width: 100%;\n	font: inherit;\n}\n\n.editorFooter {\n	padding: 0.25em 0em;\n	font-size: .9em;\n}\n\n.editorFooter .button {\npadding-top: 0px; padding-bottom: 0px;}\n\n.fieldsetFix {border: 0;\npadding: 0;\nmargin: 1px 0px 1px 0px;\n}\n/*}}}*/\n/***\n!Misc rules /%==================================================%/\n***/\n/*{{{*/\n.sparkline {\n	line-height: 1em;\n}\n\n.sparktick {\n	outline: 0;\n}\n\n.zoomer {\n	font-size: 1.1em;\n	position: absolute;\n	padding: 1em;\n}\n\n.cascade {\n	font-size: 1.1em;\n	position: absolute;\n	overflow: hidden;\n}\n/*}}}*/",
	StyleSheetPrint: "@media print {\n#mainMenu, #sidebar, #messageArea {display: none ! important;}\n#displayArea {margin: 1em 1em 0em 1em;}\n/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */\nnoscript {display:none;}\n}",
	PageTemplate: "<div class='header' macro='gradient vert #18f #04b'>\n<div class='headerShadow'>\n<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;\n<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>\n</div>\n<div class='headerForeground'>\n<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;\n<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>\n</div>\n</div>\n<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>\n<div id='sidebar'>\n<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>\n<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>\n</div>\n<div id='displayArea'>\n<div id='messageArea'></div>\n<div id='tiddlerDisplay'></div>\n</div>",
	ViewTemplate: "<div class='toolbar' macro='toolbar -closeTiddler closeOthers +editTiddler permalink references jump'></div>\n<div class='title' macro='view title'></div>\n<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date [[DD MMM YYYY]]'></span> (created <span macro='view created date [[DD MMM YYYY]]'></span>)</div>\n<div class='tagging' macro='tagging'></div>\n<div class='tagged' macro='tags'></div>\n<div class='viewer' macro='view text wikified'></div>\n<div class='tagClear'></div>",
	EditTemplate: "<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>\n<div class='title' macro='view title'></div>\n<div class='editor' macro='edit title'></div>\n<div class='editor' macro='edit text'></div>\n<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>",
	MarkupPreHead: "<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'>",
	MarkupPostHead: "",
	MarkupPreBody: "",
	MarkupPostBody: ""
	};

// ---------------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------------

var params = null; // Command line parameters
var store = null; // TiddlyWiki storage
var story = null; // Main story
var formatter = null; // Default formatters for the wikifier
var anim = new Animator(); // Animation engine
var readOnly = false; // Whether we're in readonly mode
var highlightHack = null; // Embarrassing hack department...
var hadConfirmExit = false; // Don't warn more than once
var safeMode = false; // Disable all plugins and cookies

// Starting up
function main()
{
	params = getParameters();
	if(params)
		params = params.parseParams("open",null,false);
	store = new TiddlyWiki();
	invokeParamifier(params,"oninit");
	story = new Story("tiddlerDisplay","tiddler");
	addEvent(document,"click",Popup.onDocumentClick);
	saveTest();
	loadOptionsCookie();
	for(var s=0; s<config.notifyTiddlers.length; s++)
		store.addNotification(config.notifyTiddlers[s].name,config.notifyTiddlers[s].notify);
	store.loadFromDiv("storeArea","store");
	invokeParamifier(params,"onload");
	loadSystemConfig();
	formatter = new Formatter(config.formatters);
	readOnly = (window.location.protocol == "file:") ? false : config.options.chkHttpReadOnly;
	invokeParamifier(params,"onconfig");
	store.notifyAll();
	restart();
}

// Restarting
function restart()
{
	invokeParamifier(params,"onstart");
	if(story.isEmpty())
		{
		var defaultParams = store.getTiddlerText("DefaultTiddlers").parseParams("open",null,false);
		invokeParamifier(defaultParams,"onstart");
		}
	window.scrollTo(0,0);
}

function saveTest()
{
	var saveTest = document.getElementById("saveTest");
	if(saveTest.hasChildNodes())
		alert(config.messages.savedSnapshotError);
	saveTest.appendChild(document.createTextNode("savetest"));
}

function loadSystemConfig()
{
	if(safeMode)
		return;
	var configTiddlers = store.getTaggedTiddlers("systemConfig");
	for(var t=0; t<configTiddlers.length; t++)
		{
		var ex = processConfig(configTiddlers[t].text);
		if(ex)
			displayMessage(config.messages.customConfigError.format([ex,configTiddlers[t].title]));
		}
}

// Merge a custom configuration over the top of the current configuration
// Returns a string error message or null if it went OK
function processConfig(customConfig)
{
	try
		{
		if(customConfig && customConfig != "")
			window.eval(customConfig);
		}
	catch(e)
		{
		return(e.description ? e.description : e.toString());
		}
	return null;
}

function invokeMacro(place,macro,params,wikifier,tiddler)
{
	try
		{
		var m = config.macros[macro];
		if(m && m.handler)
			m.handler(place,macro,params.readMacroParams(),wikifier,params,tiddler);
		else
			createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,config.messages.missingMacro]));
		}
	catch(ex)
		{
		createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,ex.toString()]));
		}
}

// ---------------------------------------------------------------------------------
// Paramifiers
// ---------------------------------------------------------------------------------

function getParameters()
{
	var p = null;
	if(window.location.hash)
		{
		p = decodeURI(window.location.hash.substr(1));
		if(config.browser.firefoxDate == null || config.browser.firefoxDate[1] < "20051111")
			p = convertUTF8ToUnicode(p);
		}
	return p;
}

function invokeParamifier(params,handler)
{
	if(!params || params.length == undefined || params.length <= 1)
		return;
	for(var t=1; t<params.length; t++)
		{
		var p = config.paramifiers[params[t].name];
		if(p && p[handler] instanceof Function)
			p[handler](params[t].value);
		}
}

config.paramifiers = {};

config.paramifiers.start = {
	oninit: function(v) {
		safeMode = v.toLowerCase() == "safe";
		}
};

config.paramifiers.open = {
	onstart: function(v) {
		story.displayTiddler("bottom",v,null,false,false);
		}
};

config.paramifiers.search = {
	onstart: function(v) {
		story.search(v,false,false);
		}
};

config.paramifiers.tag = {
	onstart: function(v) {
		var tagged = store.getTaggedTiddlers(v,"title");
		for(var t=0; t<tagged.length; t++)
			story.displayTiddler("bottom",tagged[t].title,null,false,false);
		}
};

config.paramifiers.newTiddler = {
	onstart: function(v) {
		if(!readOnly)
			{
			story.displayTiddler(null,v,DEFAULT_EDIT_TEMPLATE);
			story.focusTiddler(v,"text");
			}
		}
};

config.paramifiers.newJournal = {
	onstart: function(v) {
		if(!readOnly)
			{
			var now = new Date();
			var title = now.formatString(v.trim());
			story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
			story.focusTiddler(title,"text");
			}
		}
};

// ---------------------------------------------------------------------------------
// Formatters
// ---------------------------------------------------------------------------------

function Formatter(formatters)
{
	this.formatters = [];
	var pattern = [];
	for(var n=0; n<formatters.length; n++)
		{
		pattern.push("(" + formatters[n].match + ")");
		this.formatters.push(formatters[n]);
		}
	this.formatterRegExp = new RegExp(pattern.join("|"),"mg");
}

config.formatterHelpers = {

	charFormatHelper: function(w)
	{
		var e = createTiddlyElement(w.output,this.element);
		w.subWikify(e,this.terminator);
	},

	inlineCssHelper:  function(w)
	{
		var styles = [];
		var lookahead = "(?:(" + config.textPrimitives.anyLetter + "+)\\(([^\\)\\|\\n]+)(?:\\):))|(?:(" + config.textPrimitives.anyLetter + "+):([^;\\|\\n]+);)";
		var lookaheadRegExp = new RegExp(lookahead,"mg");
		var hadStyle = false;
		do {
			lookaheadRegExp.lastIndex = w.nextMatch;
			var lookaheadMatch = lookaheadRegExp.exec(w.source);
			var gotMatch = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
			if(gotMatch)
				{
				var s,v;
				hadStyle = true;
				if(lookaheadMatch[1])
					{
					s = lookaheadMatch[1].unDash();
					v = lookaheadMatch[2];
					}
				else
					{
					s = lookaheadMatch[3].unDash();
					v = lookaheadMatch[4];
					}
				switch(s)
					{
					case "bgcolor": s = "backgroundColor"; break;
					}
				styles.push({style: s, value: v});
				w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
				}
		} while(gotMatch);
		return styles;
	},

	applyCssHelper: function(e,styles)
	{
		for(var t=0; t<styles.length; t++)
			{
			try
				{
				e.style[styles[t].style] = styles[t].value;
				}
			catch (ex)
				{
				}
			}
	},

	monospacedByLineHelper: function(w)
	{
		var lookaheadRegExp = new RegExp(this.lookahead,"mg");
		lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = lookaheadRegExp.exec(w.source);
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
			{
			var text = lookaheadMatch[1];
			if(config.browser.isIE)
				text = text.replace(/\n/g,"\r");
			var e = createTiddlyElement(w.output,"pre",null,null,text);
			w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
			}
	}

};

config.formatters = [
{
	name: "table",
	match: "^\\|(?:[^\\n]*)\\|(?:[fhck]?)$",
	lookahead: "^\\|([^\\n]*)\\|([fhck]?)$",
	rowTerminator: "\\|(?:[fhck]?)$\\n?",
	cellPattern: "(?:\\|([^\\n\\|]*)\\|)|(\\|[fhck]?$\\n?)",
	cellTerminator: "(?:\\x20*)\\|",
	rowTypes: {"c": "caption", "h": "thead", "": "tbody", "f": "tfoot"},
	handler: function(w)
	{
		var table = createTiddlyElement(w.output,"table");
		w.nextMatch = w.matchStart;
		var lookaheadRegExp = new RegExp(this.lookahead,"mg");
		var currRowType = null, nextRowType;
		var rowContainer, rowElement;
		var prevColumns = [];
		var rowCount = 0;
		do {
			lookaheadRegExp.lastIndex = w.nextMatch;
			var lookaheadMatch = lookaheadRegExp.exec(w.source);
			var matched = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
			if(matched)
				{
				nextRowType = lookaheadMatch[2];
				if(nextRowType == "k")
					{
					table.className = lookaheadMatch[1];
					w.nextMatch += lookaheadMatch[0].length+1;
					continue;
					}
				if(nextRowType != currRowType)
					rowContainer = createTiddlyElement(table,this.rowTypes[nextRowType]);
				currRowType = nextRowType;
				if(currRowType == "c")
					{
					if(rowCount == 0)
						rowContainer.setAttribute("align","top");
					else
						rowContainer.setAttribute("align","bottom");
					w.nextMatch = w.nextMatch + 1;
					w.subWikify(rowContainer,this.rowTerminator);
					table.insertBefore(rowContainer,table.firstChild);
					}
				else
					{
					var rowClass = (rowCount & 1) ? "oddRow" : "evenRow";
					rowElement = createTiddlyElement(rowContainer,"tr",null,rowClass);
					this.rowHandler(w,rowElement,prevColumns);
					}
				rowCount++;
				}
		} while(matched);
	},
	rowHandler: function(w,e,prevColumns)
	{
		var col = 0;
		var currColCount = 1;
		var matched;
		var cellRegExp = new RegExp(this.cellPattern,"mg");
		do {
			cellRegExp.lastIndex = w.nextMatch;
			var cellMatch = cellRegExp.exec(w.source);
			matched = cellMatch && cellMatch.index == w.nextMatch;
			if(matched)
				{
				if(cellMatch[1] == "~")
					{
					var last = prevColumns[col];
					if(last)
						{
						last.rowCount++;
						last.element.setAttribute("rowSpan",last.rowCount);
						last.element.setAttribute("rowspan",last.rowCount);
						last.element.valign = "center";
						}
					w.nextMatch = cellMatch.index + cellMatch[0].length-1;
					}
				else if(cellMatch[1] == ">")
					{
					currColCount++;
					w.nextMatch = cellMatch.index + cellMatch[0].length-1;
					}
				else if(cellMatch[2])
					{
					w.nextMatch = cellMatch.index + cellMatch[0].length;;
					break;
					}
				else
					{
					var spaceLeft = false, spaceRight = false;
					w.nextMatch++;
					var styles = config.formatterHelpers.inlineCssHelper(w);
					while(w.source.substr(w.nextMatch,1) == " ")
						{
						spaceLeft = true;
						w.nextMatch++;
						}
					var cell;
					if(w.source.substr(w.nextMatch,1) == "!")
						{
						cell = createTiddlyElement(e,"th");
						w.nextMatch++;
						}
					else
						cell = createTiddlyElement(e,"td");
					prevColumns[col] = {rowCount: 1, element: cell};
					if(currColCount > 1)
						{
						cell.setAttribute("colSpan",currColCount);
						cell.setAttribute("colspan",currColCount);
						currColCount = 1;
						}
					config.formatterHelpers.applyCssHelper(cell,styles);
					w.subWikify(cell,this.cellTerminator);
					if(w.matchText.substr(w.matchText.length-2,1) == " ")
						spaceRight = true;
					if(spaceLeft && spaceRight)
						cell.align = "center";
					else if (spaceLeft)
						cell.align = "right";
					else if (spaceRight)
						cell.align = "left";
					w.nextMatch = w.nextMatch-1;
					}
				col++;
				}
		} while(matched);
	}
},

{
	name: "rule",
	match: "^----+$\\n?",
	handler: function(w)
	{
		createTiddlyElement(w.output,"hr");
	}
},

{
	name: "heading",
	match: "^!{1,5}",
	terminator: "\\n",
	handler: function(w)
	{
		var e = createTiddlyElement(w.output,"h" + w.matchLength);
		w.subWikify(e,this.terminator);
	}
},

{
	name: "monospacedByLine",
	match: "^\\{\\{\\{\\n",
	lookahead: "^\\{\\{\\{\\n((?:^[^\\n]*\\n)+?)(^\\}\\}\\}$\\n?)",
	handler: config.formatterHelpers.monospacedByLineHelper
},

{
	name: "monospacedByLineForCSS",
	match: "^/\\*[\\{]{3}\\*/\\n",
	lookahead: "/\\*[\\{]{3}\\*/\\n*((?:^[^\\n]*\\n)+?)(\\n*^/\\*[\\}]{3}\\*/$\\n?)",
	handler: config.formatterHelpers.monospacedByLineHelper
},

{
	name: "monospacedByLineForPlugin",
	match: "^//\\{\\{\\{\\n",
	lookahead: "^//\\{\\{\\{\\n\\n*((?:^[^\\n]*\\n)+?)(\\n*^//\\}\\}\\}$\\n?)",
	handler: config.formatterHelpers.monospacedByLineHelper
},

{
	name: "monospacedByLineForTemplate",
	match: "^<!--[\\{]{3}-->\\n",
	lookahead: "<!--[\\{]{3}-->\\n*((?:^[^\\n]*\\n)+?)(\\n*^<!--[\\}]{3}-->$\\n?)",
	handler: config.formatterHelpers.monospacedByLineHelper
},

{
	name: "wikifyCommentForPlugin",
	match: "^/\\*\\*\\*\\n",
	terminator: "^\\*\\*\\*/\\n",
	handler: function(w)
	{
		w.subWikify(w.output,this.terminator);
	}
},

{
	name: "wikifyCommentForTemplate",
	match: "^<!---\\n",
	terminator: "^--->\\n",
	handler: function(w)
	{
		w.subWikify(w.output,this.terminator);
	}
},

{
	name: "quoteByBlock",
	match: "^<<<\\n",
	terminator: "^<<<(\\n|$)",
	handler: function(w)
	{
		var e = createTiddlyElement(w.output,"blockquote");
		w.subWikify(e,this.terminator);
	}
},

{
	name: "quoteByLine",
	match: "^>+",
	terminator: "\\n",
	element: "blockquote",
	handler: function(w)
	{
		var lookaheadRegExp = new RegExp(this.match,"mg");
		var placeStack = [w.output];
		var currLevel = 0;
		var newLevel = w.matchLength;
		var t;
		do {
			if(newLevel > currLevel)
				{
				for(t=currLevel; t<newLevel; t++)
					placeStack.push(createTiddlyElement(placeStack[placeStack.length-1],this.element));
				}
			else if(newLevel < currLevel)
				{
				for(t=currLevel; t>newLevel; t--)
					placeStack.pop();
				}
			currLevel = newLevel;
			w.subWikify(placeStack[placeStack.length-1],this.terminator);
			createTiddlyElement(placeStack[placeStack.length-1],"br");
			lookaheadRegExp.lastIndex = w.nextMatch;
			var lookaheadMatch = lookaheadRegExp.exec(w.source);
			var matched = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
			if(matched)
				{
				newLevel = lookaheadMatch[0].length;
				w.nextMatch += lookaheadMatch[0].length;
				}
		} while(matched);
	}
},

{
	name: "list",
	match: "^(?:(?:\\*+)|(?:#+))",
	lookahead: "^(?:(\\*+)|(#+))",
	terminator: "\\n",
	outerElement: "ul",
	itemElement: "li",
	handler: function(w)
	{
		var lookaheadRegExp = new RegExp(this.lookahead,"mg");
		w.nextMatch = w.matchStart;
		var placeStack = [w.output];
		var currType = null, newType;
		var currLevel = 0, newLevel;
		var t;
		do {
			lookaheadRegExp.lastIndex = w.nextMatch;
			var lookaheadMatch = lookaheadRegExp.exec(w.source);
			var matched = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
			if(matched)
				{
				if(lookaheadMatch[1])
					newType = "ul";
				if(lookaheadMatch[2])
					newType = "ol";
				newLevel = lookaheadMatch[0].length;
				w.nextMatch += lookaheadMatch[0].length;
				if(newLevel > currLevel)
					{
					for(t=currLevel; t<newLevel; t++)
						placeStack.push(createTiddlyElement(placeStack[placeStack.length-1],newType));
					}
				else if(newLevel < currLevel)
					{
					for(t=currLevel; t>newLevel; t--)
						placeStack.pop();
					}
				else if(newLevel == currLevel && newType != currType)
					{
						placeStack.pop();
						placeStack.push(createTiddlyElement(placeStack[placeStack.length-1],newType));
					}
				currLevel = newLevel;
				currType = newType;
				var e = createTiddlyElement(placeStack[placeStack.length-1],"li");
				w.subWikify(e,this.terminator);
				}
		} while(matched);
	}
},

{
	name: "wikiLink",
	match: config.textPrimitives.wikiLink,
	badPrefix: config.textPrimitives.anyLetter,
	handler: function(w)
	{
		var preRegExp = new RegExp(config.textPrimitives.anyLetter,"mg");
		var preMatch = null;
		if(w.matchStart > 0)
			{
			preRegExp.lastIndex = w.matchStart-1;
			preMatch = preRegExp.exec(w.source);
			}
		if(preMatch && preMatch.index == w.matchStart-1)
			w.outputText(w.output,w.matchStart,w.nextMatch);
		else if(w.matchText.substr(0,1) == config.textPrimitives.unWikiLink)
			w.outputText(w.output,w.matchStart + 1,w.nextMatch);
		else
			{
			var link = createTiddlyLink(w.output,w.matchText,false);
			w.outputText(link,w.matchStart,w.nextMatch);
			}
	}
},

{
	name: "prettyLink",
	match: "\\[\\[",
	lookahead: "\\[\\[([^\\|\\]]*?)(?:(\\]\\])|(\\|(.*?)\\]\\]))",
	terminator: "\\|",
	handler: function(w)
	{
		var lookaheadRegExp = new RegExp(this.lookahead,"mg");
		lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = lookaheadRegExp.exec(w.source)
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart && lookaheadMatch[2]) // Simple bracketted link
			{
			var link = createTiddlyLink(w.output,lookaheadMatch[1],false);
			w.outputText(link,w.nextMatch,w.nextMatch + lookaheadMatch[1].length);
			w.nextMatch += lookaheadMatch[1].length + 2;
			}
		else if(lookaheadMatch && lookaheadMatch.index == w.matchStart && lookaheadMatch[3]) // Pretty bracketted link
			{
			var e;
			if(store.tiddlerExists(lookaheadMatch[4]) || store.isShadowTiddler(lookaheadMatch[4]))
				e = createTiddlyLink(w.output,lookaheadMatch[4],false);
			else
				e = createExternalLink(w.output,lookaheadMatch[4]);
			w.outputText(e,w.nextMatch,w.nextMatch + lookaheadMatch[1].length);
			w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
			}
	}
},

{
	name: "urlLink",
	match: config.textPrimitives.urlPattern,
	handler: function(w)
	{
		var e = createExternalLink(w.output,w.matchText);
		w.outputText(e,w.matchStart,w.nextMatch);
	}
},

{
	name: "image",
	match: "\\[(?:[<]{0,1})(?:[>]{0,1})[Ii][Mm][Gg]\\[",
	lookahead: "\\[([<]{0,1})([>]{0,1})[Ii][Mm][Gg]\\[(?:([^\\|\\]]+)\\|)?([^\\[\\]\\|]+)\\](?:\\[([^\\]]*)\\]?)?(\\])",
	handler: function(w)
	{
		var lookaheadRegExp = new RegExp(this.lookahead,"mg");
		lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = lookaheadRegExp.exec(w.source);
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart) // Simple bracketted link
			{
			var e = w.output;
			if(lookaheadMatch[5])
				{
				if(store.tiddlerExists(lookaheadMatch[5]) || store.isShadowTiddler(lookaheadMatch[5]))
					e = createTiddlyLink(w.output,lookaheadMatch[5],false);
				else
					e = createExternalLink(w.output,lookaheadMatch[5]);
				addClass(e,"imageLink");
				}
			var img = createTiddlyElement(e,"img");
			if(lookaheadMatch[1])
				img.align = "left";
			else if(lookaheadMatch[2])
				img.align = "right";
			if(lookaheadMatch[3])
				img.title = lookaheadMatch[3];
			img.src = lookaheadMatch[4];
			w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
			}
	}
},

{
	name: "macro",
	match: "<<",
	lookahead: "<<([^>\\s]+)(?:\\s*)((?:[^>]|(?:>(?!>)))*)>>",
	handler: function(w)
	{
		var lookaheadRegExp = new RegExp(this.lookahead,"mg");
		lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = lookaheadRegExp.exec(w.source)
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart && lookaheadMatch[1])
			{
			w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
			invokeMacro(w.output,lookaheadMatch[1],lookaheadMatch[2],w,w.tiddler);
			}
	}
},

{
	name: "html",
	match: "<[Hh][Tt][Mm][Ll]>",
	lookahead: "<[Hh][Tt][Mm][Ll]>((?:.|\\n)*?)</[Hh][Tt][Mm][Ll]>",
	handler: function(w)
	{
		var lookaheadRegExp = new RegExp(this.lookahead,"mg");
		lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = lookaheadRegExp.exec(w.source)
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
			{
			var e = createTiddlyElement(w.output,"span");
			e.innerHTML = lookaheadMatch[1];
			w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
			}
	}
},

{
	name: "commentByBlock",
	match: "/%",
	lookahead: "/%((?:.|\\n)*?)%/",
	handler: function(w)
	{
		var lookaheadRegExp = new RegExp(this.lookahead,"mg");
		lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = lookaheadRegExp.exec(w.source)
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
			w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
	}
},

{
	name: "boldByChar",
	match: "''",
	terminator: "''",
	element: "strong",
	handler: config.formatterHelpers.charFormatHelper
},

{
	name: "strikeByChar",
	match: "==",
	terminator: "==",
	element: "strike",
	handler: config.formatterHelpers.charFormatHelper
},

{
	name: "underlineByChar",
	match: "__",
	terminator: "__",
	element: "u",
	handler: config.formatterHelpers.charFormatHelper
},

{
	name: "italicByChar",
	match: "//",
	terminator: "//",
	element: "em",
	handler: config.formatterHelpers.charFormatHelper
},

{
	name: "subscriptByChar",
	match: "~~",
	terminator: "~~",
	element: "sub",
	handler: config.formatterHelpers.charFormatHelper
},

{
	name: "superscriptByChar",
	match: "\\^\\^",
	terminator: "\\^\\^",
	element: "sup",
	handler: config.formatterHelpers.charFormatHelper
},

{
	name: "monospacedByChar",
	match: "\\{\\{\\{",
	lookahead: "\\{\\{\\{((?:.|\\n)*?)\\}\\}\\}",
	handler: function(w)
	{
		var lookaheadRegExp = new RegExp(this.lookahead,"mg");
		lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = lookaheadRegExp.exec(w.source)
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
			{
			var e = createTiddlyElement(w.output,"code",null,null,lookaheadMatch[1]);
			w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
			}
	}
},

{
	name: "styleByChar",
	match: "@@",
	terminator: "@@",
	lookahead: "(?:([^\\(@]+)\\(([^\\)]+)(?:\\):))|(?:([^:@]+):([^;]+);)",
	handler:  function(w)
	{
		var e = createTiddlyElement(w.output,"span",null,null,null);
		var styles = config.formatterHelpers.inlineCssHelper(w);
		if(styles.length == 0)
			e.className = "marked";
		else
			config.formatterHelpers.applyCssHelper(e,styles);
		w.subWikify(e,this.terminator);
	}
},

{
	name: "lineBreak",
	match: "\\n",
	handler: function(w)
	{
		createTiddlyElement(w.output,"br");
	}
},

{
	name: "htmlEntitiesEncoding",
	match: "&#?[a-zA-Z0-9]{2,8};",
	handler: function(w)
		{
		var e = createTiddlyElement(w.output,"span");
		e.innerHTML = w.matchText ;
		}
},

{
	name: "customClasses",
	match: "\\{\\{",
	terminator: "\\}\\}\\}",
	lookahead: "\\{\\{[\\s]*([\\w]+[\\s\\w]*)[\\s]*\\{(\\n{0,1})",
	handler: function(w)
	{
		var lookaheadRegExp = new RegExp(this.lookahead,"g");
		lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = lookaheadRegExp.exec(w.source);
		if(lookaheadMatch)
			{
			var isByLine = lookaheadMatch[2] == "\n";
			var p = createTiddlyElement(w.output,isByLine ? "div" : "span",null,lookaheadMatch[1]);
			w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
			w.subWikify(p,this.terminator);
			}
	}
}

];

// ---------------------------------------------------------------------------------
// Wikifier
// ---------------------------------------------------------------------------------

function wikify(source,output,highlightRegExp,tiddler)
{
	if(source && source != "")
		{
		var wikifier = new Wikifier(source,formatter,highlightRegExp,tiddler);
		wikifier.subWikify(output,null);
		}
}

// Wikify a named tiddler to plain text
function wikifyPlain(title)
{
	if(store.tiddlerExists(title) || store.isShadowTiddler(title))
		{
		var wikifier = new Wikifier(store.getTiddlerText(title),formatter,null,store.getTiddler(title));
		return wikifier.wikifyPlain();
		}
	else
		return "";
}

// Highlight plain text into an element
function highlightify(source,output,highlightRegExp)
{
	if(source && source != "")
		{
		var wikifier = new Wikifier(source,formatter,highlightRegExp,null);
		wikifier.outputText(output,0,source.length);
		}
}

// Construct a wikifier object
// source - source string that's going to be wikified
// formatter - Formatter() object containing the list of formatters to be used
// highlightRegExp - regular expression of the text string to highlight
// tiddler - reference to the tiddler that's taken to be the container for this wikification
function Wikifier(source,formatter,highlightRegExp,tiddler)
{
	this.source = source;
	this.output = null;
	this.formatter = formatter;
	this.nextMatch = 0;
	this.highlightRegExp = highlightRegExp;
	this.highlightMatch = null;
	if(highlightRegExp)
		{
		highlightRegExp.lastIndex = 0;
		this.highlightMatch = highlightRegExp.exec(source);
		}
	this.tiddler = tiddler;
}

Wikifier.prototype.wikifyPlain = function()
{
	var e = createTiddlyElement(document.body,"div");
	e.style.display = "none";
	this.subWikify(e,null);
	var text = getPlainText(e);
	e.parentNode.removeChild(e);
	return text;
}

Wikifier.prototype.subWikify = function(output,terminator)
{
	// Temporarily replace the output pointer
	var oldOutput = this.output;
	this.output = output;
	// Prepare the terminator RegExp
	var terminatorRegExp = terminator ? new RegExp("(" + terminator + ")","mg") : null;
	do {
		// Prepare the RegExp match positions
		this.formatter.formatterRegExp.lastIndex = this.nextMatch;
		if(terminatorRegExp)
			terminatorRegExp.lastIndex = this.nextMatch;
		// Get the first matches
		var formatterMatch = this.formatter.formatterRegExp.exec(this.source);
		var terminatorMatch = terminatorRegExp ? terminatorRegExp.exec(this.source) : null;
		// Check for a terminator match
		if(terminatorMatch && (!formatterMatch || terminatorMatch.index <= formatterMatch.index))
			{
			// Output any text before the match
			if(terminatorMatch.index > this.nextMatch)
				this.outputText(this.output,this.nextMatch,terminatorMatch.index);
			// Set the match parameters
			this.matchStart = terminatorMatch.index;
			this.matchLength = terminatorMatch[1].length;
			this.matchText = terminatorMatch[1];
			this.nextMatch = terminatorMatch.index + terminatorMatch[1].length;
			// Restore the output pointer and exit
			this.output = oldOutput;
			return;
			}
		// Check for a formatter match
		else if(formatterMatch)
			{
			// Output any text before the match
			if(formatterMatch.index > this.nextMatch)
				this.outputText(this.output,this.nextMatch,formatterMatch.index);
			// Set the match parameters
			this.matchStart = formatterMatch.index;
			this.matchLength = formatterMatch[0].length;
			this.matchText = formatterMatch[0];
			this.nextMatch = this.formatter.formatterRegExp.lastIndex;
			// Figure out which formatter matched
			var matchingFormatter = -1;
			for(var t=1; t<formatterMatch.length; t++)
				if(formatterMatch[t])
					matchingFormatter = t-1;
			// Call the formatter
			if(matchingFormatter != -1)
				this.formatter.formatters[matchingFormatter].handler(this);
			}
	} while(terminatorMatch || formatterMatch);
	// Output any text after the last match
	if(this.nextMatch < this.source.length)
		{
		this.outputText(this.output,this.nextMatch,this.source.length);
		this.nextMatch = this.source.length;
		}
	// Restore the output pointer
	this.output = oldOutput;
}

Wikifier.prototype.outputText = function(place,startPos,endPos)
{
	// Check for highlights
	while(this.highlightMatch && (this.highlightRegExp.lastIndex > startPos) && (this.highlightMatch.index < endPos) && (startPos < endPos))
		{
		// Deal with any plain text before the highlight
		if(this.highlightMatch.index > startPos)
			{
			createTiddlyText(place,this.source.substring(startPos,this.highlightMatch.index));
			startPos = this.highlightMatch.index;
			}
		// Deal with the highlight
		var highlightEnd = Math.min(this.highlightRegExp.lastIndex,endPos);
		var theHighlight = createTiddlyElement(place,"span",null,"highlight",this.source.substring(startPos,highlightEnd));
		startPos = highlightEnd;
		// Nudge along to the next highlight if we're done with this one
		if(startPos >= this.highlightRegExp.lastIndex)
			this.highlightMatch = this.highlightRegExp.exec(this.source);
		}
	// Do the unhighlighted text left over
	if(startPos < endPos)
		{
		createTiddlyText(place,this.source.substring(startPos,endPos));
		}
}

// ---------------------------------------------------------------------------------
// Macro definitions
// ---------------------------------------------------------------------------------

config.macros.today.handler = function(place,macroName,params)
{
	var now = new Date();
	var text;
	if(params[0])
		text = now.formatString(params[0].trim());
	else
		text = now.toLocaleString();
	createTiddlyElement(place,"span",null,null,text);
}

config.macros.version.handler = function(place)
{
	createTiddlyElement(place,"span",null,null,version.major + "." + version.minor + "." + version.revision + (version.beta ? " (beta " + version.beta + ")" : ""));
}

config.macros.list.handler = function(place,macroName,params)
{
	var type = params[0] ? params[0] : "all";
	var theList = document.createElement("ul");
	place.appendChild(theList);
	if(this[type].prompt)
		createTiddlyElement(theList,"li",null,"listTitle",this[type].prompt);
	var results;
	if(this[type].handler)
		results = this[type].handler(params);
	for (var t = 0; t < results.length; t++)
		{
		theListItem = document.createElement("li")
		theList.appendChild(theListItem);
		if(typeof results[t] == "string")
			createTiddlyLink(theListItem,results[t],true);
		else
			createTiddlyLink(theListItem,results[t].title,true);
		}
}

config.macros.list.all.handler = function(params)
{
	return store.reverseLookup("tags","excludeLists",false,"title");
}

config.macros.list.missing.handler = function(params)
{
	return store.getMissingLinks();
}

config.macros.list.orphans.handler = function(params)
{
	return store.getOrphans();
}

config.macros.list.shadowed.handler = function(params)
{
	return store.getShadowed();
}

config.macros.allTags.handler = function(place,macroName,params)
{
	var tags = store.getTags();
	var theDateList = createTiddlyElement(place,"ul",null,null,null);
	if(tags.length == 0)
		createTiddlyElement(theDateList,"li",null,"listTitle",this.noTags);
	for(var t=0; t<tags.length; t++)
		{
		var theListItem =createTiddlyElement(theDateList,"li",null,null,null);
		var theTag = createTiddlyButton(theListItem,tags[t][0] + " (" + tags[t][1] + ")",this.tooltip.format([tags[t][0]]),onClickTag);
		theTag.setAttribute("tag",tags[t][0]);
		}
}

config.macros.timeline.handler = function(place,macroName,params)
{
	var field = params[0] ? params[0] : "modified";
	var tiddlers = store.reverseLookup("tags","excludeLists",false,field);
	var lastDay = "";
	var last = params[1] ? tiddlers.length-Math.min(tiddlers.length,parseInt(params[1])) : 0;
	for(var t=tiddlers.length-1; t>=last; t--)
		{
		var tiddler = tiddlers[t];
		var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
		if(theDay != lastDay)
			{
			var theDateList = document.createElement("ul");
			place.appendChild(theDateList);
			createTiddlyElement(theDateList,"li",null,"listTitle",tiddler[field].formatString(this.dateFormat));
			lastDay = theDay;
			}
		var theDateListItem = createTiddlyElement(theDateList,"li",null,"listLink",null);
		theDateListItem.appendChild(createTiddlyLink(place,tiddler.title,true));
		}
}

config.macros.search.handler = function(place,macroName,params)
{
	var searchTimeout = null;
	var btn = createTiddlyButton(place,this.label,this.prompt,this.onClick);
	var txt = createTiddlyElement(place,"input",null,null,null);
	if(params[0])
		txt.value = params[0];
	txt.onkeyup = this.onKeyPress;
	txt.onfocus = this.onFocus;
	txt.setAttribute("size",this.sizeTextbox);
	txt.setAttribute("accessKey",this.accessKey);
	txt.setAttribute("autocomplete","off");
	txt.setAttribute("lastSearchText","");
	if(config.browser.isSafari)
		{
		txt.setAttribute("type","search");
		txt.setAttribute("results","5");
		}
	else
		txt.setAttribute("type","text");
}

// Global because there's only ever one outstanding incremental search timer
config.macros.search.timeout = null;

config.macros.search.doSearch = function(txt)
{
	if(txt.value.length > 0)
		{
		story.search(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch);
		txt.setAttribute("lastSearchText",txt.value);
		}
}

config.macros.search.onClick = function(e)
{
	config.macros.search.doSearch(this.nextSibling);
	return false;
}

config.macros.search.onKeyPress = function(e)
{
	if (!e) var e = window.event;
	switch(e.keyCode)
		{
		case 27:
			this.value = "";
			clearMessage();
			break;
		}
	if(this.value.length > 2)
		{
		if(this.value != this.getAttribute("lastSearchText"))
			{
			if(config.macros.search.timeout)
				clearTimeout(config.macros.search.timeout);
			var txt = this;
			config.macros.search.timeout = setTimeout(function() {config.macros.search.doSearch(txt);},500);
			}
		}
	else
		{
		if(config.macros.search.timeout)
			clearTimeout(config.macros.search.timeout);
		}
}

config.macros.search.onFocus = function(e)
{
	this.select();
}

config.macros.tiddler.handler = function(place,macroName,params)
{
	var wrapper = createTiddlyElement(place,"span",null,params[1] ? params[1] : null,null);
	var text = store.getTiddlerText(params[0]);
	if(text)
		{
		var tiddlerName = params[0];
		var stack = config.macros.tiddler.tiddlerStack;
		if(stack.find(tiddlerName) !== null)
			return;
		stack.push(tiddlerName);
		try
			{
			wikify(text,wrapper,null,store.getTiddler(tiddlerName));
			}
		finally
			{
			stack.pop();
			}
		}
}

config.macros.tiddler.tiddlerStack = [];

config.macros.tag.handler = function(place,macroName,params)
{
	createTagButton(place,params[0]);
}

config.macros.tags.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
	var theList = createTiddlyElement(place,"ul");
	if(params[0] && store.tiddlerExists[params[0]])
		tiddler = store.getTiddler(params[0]);
	var lingo = config.views.wikified.tag;
	var prompt = tiddler.tags.length == 0 ? lingo.labelNoTags : lingo.labelTags;
	createTiddlyElement(theList,"li",null,"listTitle",prompt.format([tiddler.title]));
	for(var t=0; t<tiddler.tags.length; t++)
		createTagButton(createTiddlyElement(theList,"li"),tiddler.tags[t],tiddler.title);
}

config.macros.tagging.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
	var theList = createTiddlyElement(place,"ul");
	var title = "";
	if(tiddler instanceof Tiddler)
        title = tiddler.title;
	if(params[0])
        title = params[0];
	theList.setAttribute("title",this.tooltip.format([title]));
	var tagged = store.getTaggedTiddlers(title);
	var prompt = tagged.length == 0 ? this.labelNotTag : this.label;
	createTiddlyElement(theList,"li",null,"listTitle",prompt.format([title]));
	for(var t=0; t<tagged.length; t++)
		createTiddlyLink(createTiddlyElement(theList,"li"),tagged[t].title,true);
}

config.macros.closeAll.handler = function(place)
{
	createTiddlyButton(place,this.label,this.prompt,this.onClick);
}

config.macros.closeAll.onClick = function(e)
{
	story.closeAllTiddlers();
	return false;
}

config.macros.permaview.handler = function(place)
{
	createTiddlyButton(place,this.label,this.prompt,this.onClick);
}

config.macros.permaview.onClick = function(e)
{
	story.permaView();
	return false;
}

config.macros.saveChanges.handler = function(place)
{
	if(!readOnly)
		createTiddlyButton(place,this.label,this.prompt,this.onClick,null,null,this.accessKey);
}

config.macros.saveChanges.onClick = function(e)
{
	saveChanges();
	return false;
}

config.macros.slider.onClickSlider = function(e)
{
	if (!e) var e = window.event;
	var n = this.nextSibling;
	var cookie = n.getAttribute("cookie");
	var isOpen = n.style.display != "none";
	if(config.options.chkAnimate)
		anim.startAnimating(new Slider(n,!isOpen,e.shiftKey || e.altKey,"none"));
	else
		n.style.display = isOpen ? "none" : "block";
	config.options[cookie] = !isOpen;
	saveOptionCookie(cookie);
	return false;
}

config.macros.slider.createSlider = function(place,cookie,title,tooltip)
{
	var cookie = cookie ? cookie : "";
	var btn = createTiddlyButton(place,title,tooltip,this.onClickSlider);
	var panel = createTiddlyElement(place,"div",null,"sliderPanel",null);
	panel.setAttribute("cookie",cookie);
	panel.style.display = config.options[cookie] ? "block" : "none";
	return panel;
}

config.macros.slider.handler = function(place,macroName,params)
{
	var panel = this.createSlider(place,params[0],params[2],params[3]);
	var text = store.getTiddlerText(params[1]);
	if(text)
		wikify(text,panel,null,store.getTiddler(params[1]));
}

config.macros.option.onChangeOption = function(e)
{
	var opt = this.getAttribute("option");
	var elementType,valueField;
	if(opt)
		{
		switch(opt.substr(0,3))
			{
			case "txt":
				elementType = "input";
				valueField = "value";
				break;
			case "chk":
				elementType = "input";
				valueField = "checked";
				break;
			}
		config.options[opt] = this[valueField];
		saveOptionCookie(opt);
		var nodes = document.getElementsByTagName(elementType);
		for(var t=0; t<nodes.length; t++)
			{
			var optNode = nodes[t].getAttribute("option");
			if(opt == optNode)
				nodes[t][valueField] = this[valueField];
			}
		}
	return(true);
}

config.macros.option.handler = function(place,macroName,params)
{
	var opt = params[0];
	if(config.options[opt] == undefined)
		return;
	var c;
	switch(opt.substr(0,3))
		{
		case "txt":
			c = document.createElement("input");
			c.onkeyup = this.onChangeOption;
			c.setAttribute("option",opt);
			c.size = 15;
			place.appendChild(c);
			c.value = config.options[opt];
			break;
		case "chk":
			c = document.createElement("input");
			c.setAttribute("type","checkbox");
			c.onclick = this.onChangeOption;
			c.setAttribute("option",opt);
			place.appendChild(c);
			c.checked = config.options[opt];
			break;
		}
}

config.macros.newTiddler.onClick = function()
{
	story.displayTiddler(null,config.macros.newTiddler.title,DEFAULT_EDIT_TEMPLATE);
	story.focusTiddler(config.macros.newTiddler.title,"title");
	return false;
}

config.macros.newTiddler.handler = function(place)
{
	if(!readOnly)
		createTiddlyButton(place,this.label,this.prompt,this.onClick,null,null,this.accessKey);
}

config.macros.newJournal.handler = function(place,macroName,params)
{
	if(!readOnly)
		{
		var now = new Date();
		var title = now.formatString(params[0].trim());
		var btn = createTiddlyButton(place,this.label,this.prompt,this.createJournal,null,null,this.accessKey);
		btn.setAttribute("journalTitle",title);
		btn.setAttribute("params",params.join("|"));
		}
}

config.macros.newJournal.createJournal = function(e)
{
	var title = this.getAttribute("journalTitle");
	var params = this.getAttribute("params").split("|");
	story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
	for(var t=1;t<params.length;t++)
		story.setTiddlerTag(title,params[t],+1);
	story.focusTiddler(title,"text");
	return false;
}

config.macros.sparkline.handler = function(place,macroName,params)
{
	var data = [];
	var min = 0;
	var max = 0;
	for(var t=0; t<params.length; t++)
		{
		var v = parseInt(params[t]);
		if(v < min)
			min = v;
		if(v > max)
			max = v;
		data.push(v);
		}
	if(data.length < 1)
		return;
	var box = createTiddlyElement(place,"span",null,"sparkline",String.fromCharCode(160));
	box.title = data.join(",");
	var w = box.offsetWidth;
	var h = box.offsetHeight;
	box.style.paddingRight = (data.length * 2 - w) + "px";
	box.style.position = "relative";
	for(var d=0; d<data.length; d++)
		{
		var tick = document.createElement("img");
		tick.border = 0;
		tick.className = "sparktick";
		tick.style.position = "absolute";
		tick.src = "data:image/gif,GIF89a%01%00%01%00%91%FF%00%FF%FF%FF%00%00%00%C0%C0%C0%00%00%00!%F9%04%01%00%00%02%00%2C%00%00%00%00%01%00%01%00%40%02%02T%01%00%3B";
		tick.style.left = d*2 + "px";
		tick.style.width = "2px";
		var v = Math.floor(((data[d] - min)/(max-min)) * h);
		tick.style.top = (h-v) + "px";
		tick.style.height = v + "px";
		box.appendChild(tick);
		}
}

config.macros.tabs.handler = function(place,macroName,params)
{
	var cookie = params[0];
	var numTabs = (params.length-1)/3;
	var wrapper = createTiddlyElement(place,"div",null,cookie,null);
	var tabset = createTiddlyElement(wrapper,"div",null,"tabset",null);
	tabset.setAttribute("cookie",cookie);
	var validTab = false;
	for(var t=0; t<numTabs; t++)
		{
		var label = params[t*3+1];
		var prompt = params[t*3+2];
		var content = params[t*3+3];
		var tab = createTiddlyButton(tabset,label,prompt,this.onClickTab,"tab tabUnselected");
		tab.setAttribute("tab",label);
		tab.setAttribute("content",content);
		tab.title = prompt;
		if(config.options[cookie] == label)
			validTab = true;
		}
	if(!validTab)
		config.options[cookie] = params[1];
	this.switchTab(tabset,config.options[cookie]);
}

config.macros.tabs.onClickTab = function(e)
{
	config.macros.tabs.switchTab(this.parentNode,this.getAttribute("tab"));
	return false;
}

config.macros.tabs.switchTab = function(tabset,tab)
{
	var cookie = tabset.getAttribute("cookie");
	var theTab = null
	var nodes = tabset.childNodes;
	for(var t=0; t<nodes.length; t++)
		if(nodes[t].getAttribute && nodes[t].getAttribute("tab") == tab)
			{
			theTab = nodes[t];
			theTab.className = "tab tabSelected";
			}
		else
			nodes[t].className = "tab tabUnselected"
	if(theTab)
		{
		if(tabset.nextSibling && tabset.nextSibling.className == "tabContents")
			tabset.parentNode.removeChild(tabset.nextSibling);
		var tabContent = createTiddlyElement(null,"div",null,"tabContents",null);
		tabset.parentNode.insertBefore(tabContent,tabset.nextSibling);
		var contentTitle = theTab.getAttribute("content");
		wikify(store.getTiddlerText(contentTitle),tabContent,null,store.getTiddler(contentTitle));
		if(cookie)
			{
			config.options[cookie] = tab;
			saveOptionCookie(cookie);
			}
		}
}

// <<gradient [[tiddler name]] vert|horiz rgb rgb rgb rgb... >>
config.macros.gradient.handler = function(place,macroName,params,wikifier)
{
	var terminator = ">>";
	var panel;
	if(wikifier)
		panel = createTiddlyElement(place,"div",null,"gradient",null);
	else
		panel = place;
	panel.style.position = "relative";
	panel.style.overflow = "hidden";
	panel.style.zIndex = "0";
	var t;
	if(wikifier)
		{
		var styles = config.formatterHelpers.inlineCssHelper(wikifier);
		config.formatterHelpers.applyCssHelper(panel,styles);
		}
	var colours = [];
	for(t=1; t<params.length; t++)
		{
		var c = new RGB(params[t]);
		if(c)
			colours.push(c);
		}
	drawGradient(panel,params[0] != "vert",colours);
	if(wikifier)
		wikifier.subWikify(panel,terminator);
	if(document.all)
		{
		panel.style.height = "100%";
		panel.style.width = "100%";
		}
}

config.macros.message.handler = function(place,macroName,params)
{
	if(params[0])
		{
		var m = config;
		var p = params[0].split(".");
		for(var t=0; t<p.length; t++)
			{
			if(p[t] in m)
				m = m[p[t]];
			else
				break;
			}
		createTiddlyText(place,m.toString().format(params.splice(1)));
		}
}

config.macros.view.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
	if((tiddler instanceof Tiddler) && params[0] && (tiddler[params[0]] != undefined))
		{
		switch(params[1])
			{
			case undefined:
				highlightify(tiddler[params[0]],place,highlightHack);
				break;
			case "link":
				createTiddlyLink(place,tiddler[params[0]],true);
				break;
			case "wikified":
				wikify(tiddler[params[0]],place,highlightHack,tiddler);
				break;
			case "date":
				if(params[2])
					createTiddlyText(place,tiddler[params[0]].formatString(params[2]));
				else
					createTiddlyText(place,tiddler[params[0]]);
				break;
			}
		}
}

config.macros.edit.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
	var field = params[0];
	if((tiddler instanceof Tiddler) && field && (tiddler[field] != undefined))
		{
		story.setDirty(tiddler.title,true);
		switch(field)
			{
			case "title":
				var e = createTiddlyElement(place,"input");
				if(tiddler.isReadOnly())
					e.setAttribute("readOnly","readOnly");
				e.setAttribute("edit","title");
				e.setAttribute("type","text");
				e.value = tiddler.title;
				e.setAttribute("size","40");
				e.setAttribute("autocomplete","off");
				break;
			case "text":
				var wrapper1 = createTiddlyElement(place,"fieldset",null,"fieldsetFix",null);
				var wrapper2 = createTiddlyElement(wrapper1,"div",null,null,null);
				var e = createTiddlyElement(wrapper2,"textarea");
				if(tiddler.isReadOnly())
					e.setAttribute("readOnly","readOnly");
				e.value = tiddler.text;
				var rows = 10;
				var lines = tiddler.text.match(regexpNewLine);
				var maxLines = Math.max(parseInt(config.options.txtMaxEditRows),5);
				if(lines != null && lines.length > rows)
					rows = lines.length + 5;
				rows = Math.min(rows,maxLines);
				e.setAttribute("rows",rows);
				e.setAttribute("edit","text");
				break;
			case "tags":
				var e = createTiddlyElement(place,"input");
				if(tiddler.isReadOnly())
					e.setAttribute("readOnly","readOnly");
				e.setAttribute("edit","tags");
				e.setAttribute("type","text");
				e.value = tiddler.getTags();
				e.setAttribute("size","40");
				e.setAttribute("autocomplete","off");
				break;
			}
		}
}

config.macros.tagChooser.onClick = function(e)
{
	if (!e) var e = window.event;
	var lingo = config.views.editor.tagChooser;
	var popup = Popup.create(this);
	var tags = store.getTags();
	if(tags.length == 0)
		createTiddlyText(createTiddlyElement(popup,"li"),lingo.popupNone);
	for (var t=0; t<tags.length; t++)
		{
		var theTag = createTiddlyButton(createTiddlyElement(popup,"li"),tags[t][0],lingo.tagTooltip.format([tags[t][0]]),config.macros.tagChooser.onTagClick);
		theTag.setAttribute("tag",tags[t][0]);
		theTag.setAttribute("tiddler", this.getAttribute("tiddler"));
		}
	Popup.show(popup,false);
	e.cancelBubble = true;
	if (e.stopPropagation) e.stopPropagation();
	return(false);
}

config.macros.tagChooser.onTagClick = function(e)
{
	if (!e) var e = window.event;
	var tag = this.getAttribute("tag");
	var title = this.getAttribute("tiddler");
	var tiddler = store.getTiddler(title);
	if(!readOnly)
		story.setTiddlerTag(title,tag,0);
	return(false);
}

config.macros.tagChooser.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
	if(tiddler instanceof Tiddler)
		{
		var title = tiddler.title;
		var lingo = config.views.editor.tagChooser;
		var btn = createTiddlyButton(place,lingo.text,lingo.tooltip,this.onClick);
		btn.setAttribute("tiddler", title);
		}
}

// Create a toolbar command button
// place - parent DOM element
// command - reference to config.commands[] member -or- name of member
// tiddler - reference to tiddler that toolbar applies to
// theClass - the class to give the button
config.macros.toolbar.createCommand = function(place,commandName,tiddler,theClass)
{
	if(typeof commandName != "string")
		{
		var c = null;
		for(var t in config.commands)
			if(config.commands[t] == commandName)
				c = t;
		commandName = c;
		}
	if((tiddler instanceof Tiddler) && (typeof commandName == "string"))
		{
		var title = tiddler.title;
		var command = config.commands[commandName];
		var ro = tiddler.isReadOnly();
		var text = ro && command.readOnlyText ? command.readOnlyText : command.text;
		var tooltip = ro && command.readOnlyTooltip ? command.readOnlyTooltip : command.tooltip;
		if(!ro || (ro && !command.hideReadOnly))
			{
			var btn = createTiddlyButton(place,text,tooltip,this.onClickCommand);
			btn.setAttribute("commandName", commandName);
			btn.setAttribute("tiddler", title);
			if(theClass)
				addClass(btn,theClass);
			}
		}
}

config.macros.toolbar.onClickCommand = function(e)
{
	if (!e) var e = window.event;
	var command = config.commands[this.getAttribute("commandName")];
	return command.handler(e,this,this.getAttribute("tiddler"));
}

// Invoke the first command encountered from a given place that is tagged with a specified class
config.macros.toolbar.invokeCommand = function(place,theClass,event)
{
	var children = place.getElementsByTagName("a")
	for (var t=0; t<children.length; t++)
		{
		var c = children[t];
		if(hasClass(c,theClass) && c.getAttribute && c.getAttribute("commandName"))
			{
			if(c.onclick instanceof Function)
				c.onclick.call(c,event);
			break;
			}
		}
}

config.macros.toolbar.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
	for(var t=0; t<params.length; t++)
		{
		var c = params[t];
		var theClass = "";
		switch(c.substr(0,1))
			{
			case "+":
				theClass = "defaultCommand";
				c = c.substr(1);
				break;
			case "-":
				theClass = "cancelCommand";
				c = c.substr(1);
				break;
			}
		if(c in config.commands)
			this.createCommand(place,c,tiddler,theClass);
		}
}

config.macros.br.handler = function(place)
{
	createTiddlyElement(place,"br");
}

// ---------------------------------------------------------------------------------
// Menu and toolbar commands
// ---------------------------------------------------------------------------------

config.commands.closeTiddler.handler = function(event,src,title)
{
	story.closeTiddler(title,true,event.shiftKey || event.altKey);
	return false;
}

config.commands.closeOthers.handler = function(event,src,title)
{
	story.closeAllTiddlers(title);
	return false;
}

config.commands.editTiddler.handler = function(event,src,title)
{
	clearMessage();
	story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
	story.focusTiddler(title,"text");
	return false;
}

config.commands.saveTiddler.handler = function(event,src,title)
{
	var newTitle = story.saveTiddler(title,event.shiftKey);
	if(newTitle)
	   story.displayTiddler(null,newTitle);
	return false;
}

config.commands.cancelTiddler.handler = function(event,src,title)
{
	if(story.hasChanges(title) && !readOnly)
		if(!confirm(this.warning.format([title])))
			return false;
	story.setDirty(title,false);
	story.displayTiddler(null,title);
	return false;
}

config.commands.deleteTiddler.handler = function(event,src,title)
{
	var deleteIt = true;
	if (config.options.chkConfirmDelete)
		deleteIt = confirm(this.warning.format([title]));
	if (deleteIt)
		{
		store.removeTiddler(title);
		story.closeTiddler(title,true,event.shiftKey || event.altKey);
		if(config.options.chkAutoSave)
			saveChanges();
		}
	return false;
}

config.commands.permalink.handler = function(event,src,title)
{
	var t = encodeURIComponent(String.encodeTiddlyLink(title));
	if(window.location.hash != t)
		window.location.hash = t;
	return false;
}

config.commands.references.handler = function(event,src,title)
{
	var popup = Popup.create(src);
	if(popup)
		{
		var references = store.getReferringTiddlers(title);
		var c = false;
		for(var r=0; r<references.length; r++)
			if(references[r].title != title && !references[r].isTagged("excludeLists"))
				{
				createTiddlyLink(createTiddlyElement(popup,"li"),references[r].title,true);
				c = true;
				}
		if(!c)
			createTiddlyText(createTiddlyElement(popup,"li",null,"disabled"),this.popupNone);
		}
	Popup.show(popup,false);
	event.cancelBubble = true;
	if (event.stopPropagation) event.stopPropagation();
	return false;
}

config.commands.jump.handler = function(event,src,title)
{
	var popup = Popup.create(src);
	if(popup)
		{
		story.forEachTiddler(function(title,element) {
			createTiddlyLink(createTiddlyElement(popup,"li"),title,true);
			});
		}
	Popup.show(popup,false);
	event.cancelBubble = true;
	if (event.stopPropagation) event.stopPropagation();
	return false;
}

// ---------------------------------------------------------------------------------
// Tiddler() object
// ---------------------------------------------------------------------------------

function Tiddler()
{
	this.title = null;
	this.text = null;
	this.modifier = null;
	this.modified = new Date();
	this.created = new Date();
	this.links = [];
	this.tags = [];
	return this;
}

// Load a tiddler from an HTML DIV. The caller should make sure to later call Tiddler.changed()
Tiddler.prototype.loadFromDiv = function(divRef,title)
{
	var text = Tiddler.unescapeLineBreaks(divRef.firstChild ? divRef.firstChild.nodeValue : "");
	var modifier = divRef.getAttribute("modifier");
	var modified = Date.convertFromYYYYMMDDHHMM(divRef.getAttribute("modified"));
	var c = divRef.getAttribute("created");
	var created = c ? Date.convertFromYYYYMMDDHHMM(c) : modified;
	var tags = divRef.getAttribute("tags");
	this.assign(title,text,modifier,modified,tags,created);
	return this;
}

// Format the text for storage in an HTML DIV
Tiddler.prototype.saveToDiv = function()
{
	return '<div tiddler="%0" modifier="%1" modified="%2" created="%3" tags="%4">%5</div>'.format([
			this.title.htmlEncode(),
			this.modifier.htmlEncode(),
			this.modified.convertToYYYYMMDDHHMM(),
			this.created.convertToYYYYMMDDHHMM(),
			this.getTags().htmlEncode(),
			this.escapeLineBreaks().htmlEncode()
		]);
}

// Format the text for storage in an RSS item
Tiddler.prototype.saveToRss = function(url)
{
	var s = [];
	s.push("<item>");
	s.push("<title>" + this.title.htmlEncode() + "</title>");
	s.push("<description>" + this.text.replace(regexpNewLine,"<br />").htmlEncode() + "</description>");
	for(var t=0; t<this.tags.length; t++)
		s.push("<category>" + this.tags[t] + "</category>");
	s.push("<link>" + url + "#" + encodeURIComponent(String.encodeTiddlyLink(this.title)) + "</link>");
	s.push("<pubDate>" + this.modified.toGMTString() + "</pubDate>");
	s.push("</item>");
	return(s.join("\n"));
}

// Change the text and other attributes of a tiddler
Tiddler.prototype.set = function(title,text,modifier,modified,tags,created)
{
	this.assign(title,text,modifier,modified,tags,created);
	this.changed();
	return this;
}

// Change the text and other attributes of a tiddler without triggered a tiddler.changed() call
Tiddler.prototype.assign = function(title,text,modifier,modified,tags,created)
{
	if(title != undefined)
		this.title = title;
	if(text != undefined)
		this.text = text;
	if(modifier != undefined)
		this.modifier = modifier;
	if(modified != undefined)
		this.modified = modified;
	if(created != undefined)
		this.created = created;
	if(tags != undefined)
		this.tags = (typeof tags == "string") ? tags.readBracketedList() : tags;
	else if(this.tags == undefined)
		this.tags = [];
	return this;
}

// Get the tags for a tiddler as a string (space delimited, using [[brackets]] for tags containing spaces)
Tiddler.prototype.getTags = function()
{
	if(this.tags)
		{
		var results = [];
		for(var t=0; t<this.tags.length; t++)
			results.push(String.encodeTiddlyLink(this.tags[t]));
		return results.join(" ");
		}
	else
		return "";
}

// Test if a tiddler carries a tag
Tiddler.prototype.isTagged = function(tag)
{
	return this.tags.find(tag) != null;
}

var regexpBackSlashEn = new RegExp("\\\\n","mg");
var regexpBackSlash = new RegExp("\\\\","mg");
var regexpBackSlashEss = new RegExp("\\\\s","mg");
var regexpNewLine = new RegExp("\n","mg");
var regexpCarriageReturn = new RegExp("\r","mg");

// Static method to Convert "\n" to newlines, "\s" to "\"
Tiddler.unescapeLineBreaks = function(text)
{
	if(text && text != "")
		return text.replace(regexpBackSlashEn,"\n").replace(regexpBackSlashEss,"\\").replace(regexpCarriageReturn,"");
	else
		return "";
}

// Convert newlines to "\n", "\" to "\s"
Tiddler.prototype.escapeLineBreaks = function()
{
	return this.text.replace(regexpBackSlash,"\\s").replace(regexpNewLine,"\\n").replace(regexpCarriageReturn,"");
}

// Updates the secondary information (like links[] array) after a change to a tiddler
Tiddler.prototype.changed = function()
{
	this.links = [];
	var nextPos = 0;
	var theLink;
	var aliasedPrettyLink = "\\[\\[([^\\[\\]\\|]+)\\|([^\\[\\]\\|]+)\\]\\]";
	var prettyLink = "\\[\\[([^\\]]+)\\]\\]";
	var wikiNameRegExp = new RegExp("(" + config.textPrimitives.wikiLink + ")|(?:" + aliasedPrettyLink + ")|(?:" + prettyLink + ")","mg");
	do {
		var formatMatch = wikiNameRegExp.exec(this.text);
		if(formatMatch)
			{
			if(formatMatch[1] && formatMatch[1].substr(0,1) != config.textPrimitives.unWikiLink && formatMatch[1] != this.title)
				this.links.pushUnique(formatMatch[1]);
			else if(formatMatch[2] && (store.tiddlerExists(formatMatch[3]) || store.isShadowTiddler(formatMatch[3])))
				this.links.pushUnique(formatMatch[3]);
			else if(formatMatch[4] && formatMatch[4] != this.title)
				this.links.pushUnique(formatMatch[4]);
			}
	} while(formatMatch);
	return;
}

Tiddler.prototype.getSubtitle = function()
{
	var theModifier = this.modifier;
	if(!theModifier)
		theModifier = config.messages.subtitleUnknown;
	var theModified = this.modified;
	if(theModified)
		theModified = theModified.toLocaleString();
	else
		theModified = config.messages.subtitleUnknown;
	return(config.messages.tiddlerLinkTooltip.format([this.title,theModifier,theModified]));
}

Tiddler.prototype.isReadOnly = function()
{
	return readOnly;
}

// ---------------------------------------------------------------------------------
// TiddlyWiki() object contains Tiddler()s
// ---------------------------------------------------------------------------------

function TiddlyWiki()
{
	var tiddlers = {}; // Hashmap by name of tiddlers
	this.namedNotifications = []; // Array of {name:,notify:} of notification functions
	this.notificationLevel = 0;
	this.clear = function() {
		tiddlers = {};
		this.setDirty(false);
		};
	this.fetchTiddler = function(title) {
		return tiddlers[title];
		};
	this.deleteTiddler = function(title) {
		 delete tiddlers[title];
		};
	this.addTiddler = function(tiddler) {
		 tiddlers[tiddler.title] = tiddler;
		};
	this.forEachTiddler = function(callback) {
		for(var t in tiddlers)
			{
			var tiddler = tiddlers[t];
			if(tiddler instanceof Tiddler)
				callback.call(this,t,tiddler);
			}
		};
}

// Set the dirty flag
TiddlyWiki.prototype.setDirty = function(dirty)
{
	this.dirty = dirty;
}

TiddlyWiki.prototype.isDirty = function()
{
	return this.dirty;
}

TiddlyWiki.prototype.suspendNotifications = function()
{
	this.notificationLevel--;
}

TiddlyWiki.prototype.resumeNotifications = function()
{
	this.notificationLevel++;
}

// Invoke the notification handlers for a particular tiddler
TiddlyWiki.prototype.notify = function(title,doBlanket)
{
	if(!this.notificationLevel)
		for(var t=0; t<this.namedNotifications.length; t++)
			{
			var n = this.namedNotifications[t];
			if((n.name == null && doBlanket) || (n.name == title))
				n.notify(title);
			}
}

// Invoke the notification handlers for all tiddlers
TiddlyWiki.prototype.notifyAll = function()
{
	if(!this.notificationLevel)
		for(var t=0; t<this.namedNotifications.length; t++)
			{
			var n = this.namedNotifications[t];
			n.notify(n.name);
			}
}

// Add a notification handler to a tiddler
TiddlyWiki.prototype.addNotification = function(title,fn)
{
	for (var i=0; i<this.namedNotifications.length; i++)
		if((this.namedNotifications[i].name == title) && (this.namedNotifications[i].notify == fn))
			return this;
	this.namedNotifications.push({name: title, notify: fn});
	return this;
}

TiddlyWiki.prototype.removeTiddler = function(title)
{
	var tiddler = this.fetchTiddler(title);
	if(tiddler)
		{
		this.deleteTiddler(title);
		this.notify(title,true);
		this.setDirty(true);
		}
}

TiddlyWiki.prototype.tiddlerExists = function(title)
{
	var t = this.fetchTiddler(title);
	return (t != undefined);
}

TiddlyWiki.prototype.isShadowTiddler = function(title)
{
	var s = config.shadowTiddlers[title];
	return (s != undefined && typeof s == "string");
}

TiddlyWiki.prototype.getTiddler = function(title)
{
	var t = this.fetchTiddler(title);
	if(t != undefined)
		return t;
	else
		return null;
}

TiddlyWiki.prototype.getTiddlerText = function(title,defaultText)
{
	if(!title)
		return(defaultText);
	var tiddler = this.fetchTiddler(title);
	if(tiddler)
		return tiddler.text;
	else if(this.isShadowTiddler(title))
		return config.shadowTiddlers[title];
	else if(defaultText != undefined)
		return defaultText;
	else
		return null;
}

TiddlyWiki.prototype.getRecursiveTiddlerText = function(title,defaultText,depth)
{
	var bracketRegExp = new RegExp("(?:\\[\\[([^\\]]+)\\]\\])","mg");
	var text = this.getTiddlerText(title,defaultText);
	if(text == null)
		return "";
	var textOut = [];
	var lastPos = 0;
	do {
		var match = bracketRegExp.exec(text);
		if(match)
			{
			textOut.push(text.substr(lastPos,match.index-lastPos));
			if(match[1])
				{
				if(depth <= 0)
					textOut.push(match[1]);
				else
					textOut.push(this.getRecursiveTiddlerText(match[1],match[1],depth-1));
				}
			lastPos = match.index + match[0].length;
			}
		else
			textOut.push(text.substr(lastPos));
	} while(match);
	return(textOut.join(""));
}

TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags)
{
	var tiddler = this.fetchTiddler(title);
	var created;
	if(tiddler)
		{
 		created = tiddler.created; // preserve created date
		this.deleteTiddler(title);
		}
	else
		{
		tiddler = new Tiddler();
		created = modified;
		}
	tiddler.set(newTitle,newBody,modifier,modified,tags,created);
	this.addTiddler(tiddler);
	if(title != newTitle)
		this.notify(title,true);
	this.notify(newTitle,true);
	this.setDirty(true);
	return tiddler;
}

TiddlyWiki.prototype.createTiddler = function(title)
{
	var tiddler = this.fetchTiddler(title);
	if(!tiddler)
		{
		tiddler = new Tiddler();
		tiddler.title = title;
		this.addTiddler(tiddler);
		this.setDirty(true);
		}
	return tiddler;
}

// Load contents of a tiddlywiki from an HTML DIV
TiddlyWiki.prototype.loadFromDiv = function(srcID,idPrefix)
{
	if(document.normalize)
		document.normalize();
	var lenPrefix = idPrefix.length;
	var store = document.getElementById(srcID).childNodes;
	var tiddlers = [];
	for(var t = 0; t < store.length; t++)
		{
		var e = store[t];
		var title = null;
		if(e.getAttribute)
			title = e.getAttribute("tiddler");
		if(!title && e.id && e.id.substr(0,lenPrefix) == idPrefix)
			title = e.id.substr(lenPrefix);
		if(title && title != "")
			{
			var tiddler = this.createTiddler(title);
			tiddler.loadFromDiv(e,title);
			tiddlers.push(tiddler);
			}
		}
	for(var t = 0;t<tiddlers.length; t++)
		tiddlers[t].changed();
	this.setDirty(false);
}

// Return an array of tiddlers matching a search regular expression
TiddlyWiki.prototype.search = function(searchRegExp,sortField,excludeTag)
{
	var candidates = this.reverseLookup("tags",excludeTag,false);
	var results = [];
	for(var t=0; t<candidates.length; t++)
		{
		if((candidates[t].title.search(searchRegExp) != -1) || (candidates[t].text.search(searchRegExp) != -1))
			results.push(candidates[t]);
		}
	if(!sortField)
		sortField = "title";
	results.sort(function (a,b) {if(a[sortField] == b[sortField]) return(0); else return (a[sortField] < b[sortField]) ? -1 : +1; });
	return results;
}

// Return an array of all the tags in use. Each member of the array is another array where [0] is the name of the tag and [1] is the number of occurances
TiddlyWiki.prototype.getTags = function()
{
	var results = [];
	this.forEachTiddler(function(title,tiddler) {
		for(var g=0; g<tiddler.tags.length; g++)
			{
			var tag = tiddler.tags[g];
			var f = false;
			for(var c=0; c<results.length; c++)
				if(results[c][0] == tag)
					{
					f = true;
					results[c][1]++;
					}
			if(!f)
				results.push([tag,1]);
			}
		});
	results.sort(function (a,b) {if(a[0].toLowerCase() == b[0].toLowerCase()) return(0); else return (a[0].toLowerCase() < b[0].toLowerCase()) ? -1 : +1; });
	return results;
}

// Return an array of the tiddlers that are tagged with a given tag
TiddlyWiki.prototype.getTaggedTiddlers = function(tag,sortField)
{
	return this.reverseLookup("tags",tag,true,sortField);
}

// Return an array of the tiddlers that link to a given tiddler
TiddlyWiki.prototype.getReferringTiddlers = function(title,unusedParameter,sortField)
{
	return this.reverseLookup("links",title,true,sortField);
}

// Return an array of the tiddlers that do or do not have a specified entry in the specified storage array (ie, "links" or "tags")
// lookupMatch == true to match tiddlers, false to exclude tiddlers
TiddlyWiki.prototype.reverseLookup = function(lookupField,lookupValue,lookupMatch,sortField)
{
	var results = [];
	this.forEachTiddler(function(title,tiddler) {
		var f = !lookupMatch;
		for(var lookup=0; lookup<tiddler[lookupField].length; lookup++)
			if(tiddler[lookupField][lookup] == lookupValue)
				f = lookupMatch;
		if(f)
			results.push(tiddler);
		});
	if(!sortField)
		sortField = "title";
	results.sort(function (a,b) {if(a[sortField] == b[sortField]) return(0); else return (a[sortField] < b[sortField]) ? -1 : +1; });
	return results;
}

// Return the tiddlers as a sorted array
TiddlyWiki.prototype.getTiddlers = function(field,excludeTag)
{
	var results = [];
	this.forEachTiddler(function(title,tiddler) {
		if(excludeTag == undefined || tiddler.tags.find(excludeTag) == null)
			results.push(tiddler);
		});
	if(field)
		results.sort(function (a,b) {if(a[field] == b[field]) return(0); else return (a[field] < b[field]) ? -1 : +1; });
	return results;
}

// Return array of names of tiddlers that are referred to but not defined
TiddlyWiki.prototype.getMissingLinks = function(sortField)
{
	var results = [];
	this.forEachTiddler(function (title,tiddler) {
		if(tiddler.tags.find("systemConfig") == null && tiddler.tags.find("excludeMissing") == null)
			for(var n=0; n<tiddler.links.length;n++)
				{
				var link = tiddler.links[n];
				if(this.fetchTiddler(link) == null && !this.isShadowTiddler(link))
					results.pushUnique(link);
				}
		});
	results.sort();
	return results;
}

// Return an array of names of tiddlers that are defined but not referred to
TiddlyWiki.prototype.getOrphans = function()
{
	var results = [];
	this.forEachTiddler(function (title,tiddler) {
		if(this.getReferringTiddlers(title).length == 0 && !tiddler.isTagged("excludeLists"))
			results.push(title);
		});
	results.sort();
	return results;
}

// Return an array of names of all the shadow tiddlers
TiddlyWiki.prototype.getShadowed = function()
{
	var results = [];
	for(var t in config.shadowTiddlers)
		if(typeof config.shadowTiddlers[t] == "string")
			results.push(t);
	results.sort();
	return results;
}

// ---------------------------------------------------------------------------------
// Story functions
// ---------------------------------------------------------------------------------

function displayTiddlers(srcElement,titles,template,unused1,unused2,animate,slowly)
{
	story.displayTiddlers(srcElement,titles,template,animate,slowly);
}

function displayTiddler(srcElement,title,template,unused1,unused2,animate,slowly)
{
	story.displayTiddler(srcElement,title,template,animate,slowly);
}

// A story is a HTML div containing a sequence of tiddlers that can be manipulated
// container - id of containing element
// idPrefix - string prefix prepended to title to make ids for tiddlers in this story
function Story(container,idPrefix)
{
	this.container = container;
	this.idPrefix = idPrefix;
	this.highlightRegExp = null;
}

// Iterate through all the tiddlers in a story
// fn - callback function to be called for each tiddler. Arguments are:
//		tiddler - reference to Tiddler object
//		element - reference to tiddler display element
Story.prototype.forEachTiddler = function(fn)
{
	var place = document.getElementById(this.container);
	if(!place)
		return;
	var e = place.firstChild;
	while(e)
		{
		var n = e.nextSibling;
		var title = e.getAttribute("tiddler");
		fn.call(this,title,e);
		e = n;
		}
}

// Display several tiddlers given their titles in an array. Parameters same as displayTiddler(), except:
// titles - array of string titles
Story.prototype.displayTiddlers = function(srcElement,titles,template,animate,slowly)
{
	for(var t = titles.length-1;t>=0;t--)
		this.displayTiddler(srcElement,titles[t],template,animate,slowly);
}

// Display a given tiddler with a given template. If the tiddler is already displayed but with a different
// template, it is switched to the specified template
// srcElement - reference to element from which this one is being opened -or-
//              special positions "top", "bottom"
// title - title of tiddler to display
// template - the name of the tiddler containing the template -or-
//			  one of the constants DEFAULT_VIEW_TEMPLATE and DEFAULT_EDIT_TEMPLATE -or-
//			  null or undefined to indicate the current template if there is one, DEFAULT_VIEW_TEMPLATE if not
// animate - whether to perform animations
// slowly - whether to perform animations in slomo
Story.prototype.displayTiddler = function(srcElement,title,template,animate,slowly)
{
	var place = document.getElementById(this.container);
	var theTiddler = document.getElementById(this.idPrefix + title);
	if(theTiddler)
		this.refreshTiddler(title,template);
	else
		{
		var before = this.positionTiddler(srcElement);
		theTiddler = this.createTiddler(place,before,title,template);
		}
	if(srcElement)
		{
		if(config.options.chkAnimate && (animate == undefined || animate == true))
			anim.startAnimating(new Cascade(title,srcElement,theTiddler,slowly),new Scroller(theTiddler,slowly));
		else
			window.scrollTo(0,ensureVisible(theTiddler));
		}
}

// Figure out the appropriate position for a newly opened tiddler
// srcElement - reference to the element containing the link to the tiddler -or-
//              special positions "top", "bottom"
// returns - reference to the tiddler that the new one should appear before (null for the bottom of the story)
Story.prototype.positionTiddler = function(srcElement)
{
	var place = document.getElementById(this.container);
	var before;
	if(typeof srcElement == "string")
		{
		switch(srcElement)
			{
			case "top":
				before = place.firstChild;
				break;
			case "bottom":
				before = null;
				break;
			}
		}
	else
		{
		var after = this.findContainingTiddler(srcElement);
		if(after == null)
			before = place.firstChild;
		else if(after.nextSibling)
			before = after.nextSibling;
		else
			before = null;
		}
	return before;
}

// Create a tiddler frame at the appropriate place in a story column
// place - reference to parent element
// before - null, or reference to element before which to insert new tiddler
// title - title of new tiddler
// template - the name of the tiddler containing the template or one of the constants DEFAULT_VIEW_TEMPLATE and DEFAULT_EDIT_TEMPLATE
Story.prototype.createTiddler = function(place,before,title,template)
{
	var theTiddler = createTiddlyElement(null,"div",this.idPrefix + title,"tiddler",null);
	place.insertBefore(theTiddler,before);
	this.refreshTiddler(title,template);
	return theTiddler;
}

// Overridable for choosing the name of the template to apply for a tiddler
Story.prototype.chooseTemplateForTiddler = function(title,template)
{
	if(!template)
		template = DEFAULT_VIEW_TEMPLATE;
	if(template == DEFAULT_VIEW_TEMPLATE || template == DEFAULT_EDIT_TEMPLATE)
		template = config.tiddlerTemplates[template];
	return template;
}

// Overridable for extracting the text of a template from a tiddler
Story.prototype.getTemplateForTiddler = function(title,template,tiddler)
{
	return store.getTiddlerText(template);
}

// Apply a template to an existing tiddler if it is not already displayed using that template
// title - title of tiddler to update
// template - the name of the tiddler containing the template or one of the constants DEFAULT_VIEW_TEMPLATE and DEFAULT_EDIT_TEMPLATE
// force - if true, forces the refresh even if the template hasn't changedd
Story.prototype.refreshTiddler = function(title,template,force)
{
	var theTiddler = document.getElementById(this.idPrefix + title);
	if(theTiddler)
		{
		if(theTiddler.getAttribute("dirty") == "true" && !force)
			return theTiddler;
		template = this.chooseTemplateForTiddler(title,template);
		var currTemplate = theTiddler.getAttribute("template");
		if((template != currTemplate) || force)
			{
			var tiddler = store.getTiddler(title);
			if(!tiddler)
				{
				tiddler = new Tiddler();
				if(store.isShadowTiddler(title))
					tiddler.set(title,store.getTiddlerText(title),config.views.wikified.shadowModifier,version.date,[],version.date);
				else
					{
					var text = template=="EditTemplate"
								? config.views.editor.defaultText.format([title])
								: config.views.wikified.defaultText.format([title]);
					tiddler.set(title,text,config.views.wikified.defaultModifier,version.date,[],version.date);
					}
				}
			theTiddler.setAttribute("tags",tiddler.tags.join(" "));
			theTiddler.setAttribute("tiddler",title);
			theTiddler.setAttribute("template",template);
			var me = this;
			theTiddler.onmouseover = this.onTiddlerMouseOver;
			theTiddler.onmouseout = this.onTiddlerMouseOut;
			theTiddler.ondblclick = this.onTiddlerDblClick;
			theTiddler.onkeypress = this.onTiddlerKeyPress;
			var html = this.getTemplateForTiddler(title,template,tiddler);
			theTiddler.innerHTML = html;
			applyHtmlMacros(theTiddler,tiddler);
			if(store.getTaggedTiddlers(title).length > 0)
				addClass(theTiddler,"isTag");
			else
				removeClass(theTiddler,"isTag");
			if(!store.tiddlerExists(title))
				{
				if(store.isShadowTiddler(title))
					addClass(theTiddler,"shadow");
				else
					addClass(theTiddler,"missing");
				}
			else
				{
				removeClass(theTiddler,"shadow");
				removeClass(theTiddler,"missing");
				}
			}
		}
	return theTiddler;
}

// Default tiddler onmouseover/out event handlers
Story.prototype.onTiddlerMouseOver = function(e)
{
	if(window.addClass instanceof Function)
		addClass(this,"selected");
}

Story.prototype.onTiddlerMouseOut = function(e)
{
	if(window.removeClass instanceof Function)
		removeClass(this,"selected");
}

// Default tiddler ondblclick event handler
Story.prototype.onTiddlerDblClick = function(e)
{
	if (!e) var e = window.event;
	var theTarget = resolveTarget(e);
	if(theTarget && theTarget.nodeName.toLowerCase() != "input" && theTarget.nodeName.toLowerCase() != "textarea")
		{
		if(document.selection && document.selection.empty)
			document.selection.empty();
		config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
		e.cancelBubble = true;
		if (e.stopPropagation) e.stopPropagation();
		return true;
		}
	else
		return false;
}

// Default tiddler onkeypress event handler
Story.prototype.onTiddlerKeyPress = function(e)
{
	if (!e) var e = window.event;
	clearMessage();
	var consume = false;
	var title = this.getAttribute("tiddler");
	switch(e.keyCode)
		{
		case 13: // Ctrl-Enter
		case 10: // Ctrl-Enter on IE PC
		case 77: // Ctrl-Enter is "M" on some platforms
			if(e.ctrlKey)
				{
				blurElement(this);
				config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
				consume = true;
				}
			break;
		case 27: // Escape
			blurElement(this);
			config.macros.toolbar.invokeCommand(this,"cancelCommand",e);
			consume = true;
			break;
		}
	e.cancelBubble = consume;
	if(consume)
		if (e.stopPropagation) e.stopPropagation();
	return(!consume);
};

// Focus a specified tiddler. Attempts to focus the specified field, otherwise the first edit field it finds
Story.prototype.focusTiddler = function(title,field)
{
	var tiddler = document.getElementById(this.idPrefix + title);
	if(tiddler != null)
		{
		var children = tiddler.getElementsByTagName("*")
		var e = null;
		for (var t=0; t<children.length; t++)
			{
			var c = children[t];
			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
				{
				if(!e)
					e = c;
				if(c.getAttribute("edit") == field)
					e = c;
				}
			}
		if(e)
			{
			e.focus();
			e.select();
			}
		}
}

// Ensures that a specified tiddler does not have the focus
Story.prototype.blurTiddler = function (title)
{
	var tiddler = document.getElementById(this.idPrefix + title);
	if(tiddler != null && tiddler.focus && tiddler.blur)
		{
		tiddler.focus();
		tiddler.blur();
		}
}

// Adds a specified tag to the edit controls (if any) for a particular tiddler)
// title - name of tiddler
// tag - name of tag, without any [[brackets]]
// mode - +1 to add the tag, -1 to remove it, 0 to toggle it
Story.prototype.setTiddlerTag = function(title,tag,add)
{
	var tiddler = document.getElementById(this.idPrefix + title);
	if(tiddler != null)
		{
		var children = tiddler.getElementsByTagName("input")
		for (var t=0; t<children.length; t++)
			{
			var c = children[t];
			if(c.tagName.toLowerCase() == "input" && c.getAttribute("edit") == "tags")
				{
				var tags = c.value.readBracketedList();
				var p = tags.find(tag);
				if(add == 0)
				    add = (p == null) ? +1 : -1;
				if(add == +1)
					{
					if(p == null)
						tags.push(tag);
					}
				else if(add == -1)
					{
					if(p != null)
						tags.splice(p,1);
					}
				var result = [];
				for(var r=0; r<tags.length; r++)
					result.push(String.encodeTiddlyLink(tags[r]));
				c.value = result.join(" ");
				}
			}
		}
}

// Close a specified tiddler
// title - name of tiddler to close
// animate - whether to perform animations
// slowly - whether to perform animations in slomo
Story.prototype.closeTiddler = function(title,animate,slowly)
{
	var tiddler = document.getElementById(this.idPrefix + title);
	if(tiddler != null)
		{
		clearMessage();
		this.scrubTiddler(tiddler);
		if(config.options.chkAnimate && animate)
			anim.startAnimating(new Slider(tiddler,false,slowly,"all"));
		else
			tiddler.parentNode.removeChild(tiddler);
		}
}

// Scrub IDs from a tiddler. This is so that the 'ghost' of a tiddler while it is being closed
// does not interfere with things
// tiddler - reference to the tiddler element
Story.prototype.scrubTiddler = function(tiddler)
{
	tiddler.id = null;
}

// Set the 'dirty' flag of a tiddler
// tiddler - title of tiddler to change
// dirty - new boolean status of flag
Story.prototype.setDirty = function(title,dirty)
{
	var tiddler = document.getElementById(this.idPrefix + title);
	if(tiddler != null)
		tiddler.setAttribute("dirty",dirty ? "true" : "false");
}

// Is a particular tiddler dirty (with unsaved changes)?
Story.prototype.isDirty = function(title)
{
	var tiddler = document.getElementById(this.idPrefix + title);
	if(tiddler != null)
		return tiddler.getAttribute("dirty") == "true";
	return null;
}

// Close all tiddlers in the story
Story.prototype.closeAllTiddlers = function(exclude)
{
	clearMessage();
	this.forEachTiddler(function(title,element) {
		if((title != exclude) && element.getAttribute("dirty") != "true")
			this.closeTiddler(title);
		});
	window.scrollTo(0,0);
}

// Check if there are any tiddlers in the story
Story.prototype.isEmpty = function()
{
	var place = document.getElementById(this.container);
	return(place && place.firstChild == null);
}

// Perform a search and display the result
// text - text to search for
// useCaseSensitive - true for case sensitive matching
// useRegExp - true to interpret text as a RegExp
Story.prototype.search = function(text,useCaseSensitive,useRegExp)
{
	this.closeAllTiddlers();
	highlightHack = new RegExp(useRegExp ?	 text : text.escapeRegExp(),useCaseSensitive ? "mg" : "img");
	var matches = store.search(highlightHack,"title","excludeSearch");
	for(var t=matches.length-1; t>=0; t--)
		this.displayTiddler(null,matches[t].title);
	highlightHack = null;
	var q = useRegExp ? "/" : "'";
	if(matches.length > 0)
		displayMessage(config.macros.search.successMsg.format([matches.length.toString(),q + text + q]));
	else
		displayMessage(config.macros.search.failureMsg.format([q + text + q]));
}

// Determine if the specified element is within a tiddler in this story
// e - reference to an element
// returns: reference to a tiddler element or null if none
Story.prototype.findContainingTiddler = function(e)
{
	while(e && !hasClass(e,"tiddler"))
		e = e.parentNode;
	return(e);
}

// Gather any saveable fields from a tiddler element
// e - reference to an element to scan recursively
// fields - object to contain gathered field values
Story.prototype.gatherSaveFields = function(e,fields)
{
	if(e && e.getAttribute)
		{
		var f = e.getAttribute("edit");
		if(f)
			fields[f] = e.value.replace(/\r/mg,"");;
		if(e.hasChildNodes())
			{
			var c = e.childNodes;
			for(var t=0; t<c.length; t++)
				this.gatherSaveFields(c[t],fields)
			}
		}
}

// Determine whether a tiddler has any edit fields, and if so if their values have been changed
// title - name of tiddler
Story.prototype.hasChanges = function(title)
{
	var changed = false;
	var e = document.getElementById(this.idPrefix + title);
	if(e != null)
		{
		var fields = {};
		this.gatherSaveFields(e,fields);
		var tiddler = store.fetchTiddler(title);
		for(var n in fields)
			{
			switch(n)
				{
				case "title":
					if(tiddler && tiddler.title != fields.title)
						changed = true;
					break;
				case "text":
					if(tiddler && tiddler.text != fields.text)
						changed = true;
					break;
				case "tags":
					if(tiddler && tiddler.getTags() != fields.tags)
						changed = true;
					break;
				}
			}
		}
	return changed;
}

// Save any open edit fields of a tiddler and updates the display as necessary
// title - name of tiddler
// minorUpdate - true if the modified date shouldn't be updated
// returns: title of saved tiddler, or null if not saved
Story.prototype.saveTiddler = function(title,minorUpdate)
{
	var tiddler = document.getElementById(this.idPrefix + title);
	if(tiddler != null)
		{
		var fields = {};
		this.gatherSaveFields(tiddler,fields);
		var newTitle = fields.title ? fields.title : title;
		if(store.tiddlerExists(newTitle) && newTitle != title)
			{
			if(confirm(config.messages.overwriteWarning.format([newTitle.toString()])))
				this.closeTiddler(newTitle,false,false);
			else
				return null;
			}
		tiddler.id = this.idPrefix + newTitle;
		tiddler.setAttribute("tiddler",newTitle);
		tiddler.setAttribute("dirty","false");
		if(config.options.chkForceMinorUpdate)
			minorUpdate = !minorUpdate;
		var newDate = new Date();
		store.saveTiddler(title,newTitle,fields.text,config.options.txtUserName,minorUpdate ? undefined : newDate,fields.tags);
		if(config.options.chkAutoSave)
			saveChanges();
		return newTitle;
		}
	return null;
}

Story.prototype.permaView = function()
{
	var links = [];
	this.forEachTiddler(function(title,element) {
		links.push(String.encodeTiddlyLink(title));
		});
	var t = encodeURIComponent(links.join(" "));
	if(t == "")
		t = "#";
	if(window.location.hash != t)
		window.location.hash = t;
}

// ---------------------------------------------------------------------------------
// Message area
// ---------------------------------------------------------------------------------

function displayMessage(text,linkText)
{
	var msgArea = document.getElementById("messageArea");
	if(!msgArea)
		{
		alert(text);
		return;
		}
	if(!msgArea.hasChildNodes())
		createTiddlyButton(createTiddlyElement(msgArea,"div",null,"messageToolbar"),config.messages.messageClose.text,config.messages.messageClose.tooltip,clearMessage);
	var msg;
	if(linkText)
		{
		msg = createTiddlyElement(msgArea,"div",null,null,null);
		var link = createTiddlyElement(msg,"a",null,null,text);
		link.href = linkText;
		link.target = "_blank";
		}
	else
		msg = createTiddlyElement(msgArea,"div",null,null,text);
	msgArea.style.display = "block";
}

function clearMessage()
{
	var msgArea = document.getElementById("messageArea");
	if(msgArea)
		{
		removeChildren(msgArea);
		msgArea.style.display = "none";
		}
	return false;
}

// ---------------------------------------------------------------------------------
// Refresh mechanism
// ---------------------------------------------------------------------------------

config.refreshers = {
	link: function(e,changeList)
		{
		var title = e.getAttribute("tiddlyLink");
		refreshTiddlyLink(e,title);
		},

	content: function(e,changeList)
		{
		var title = e.getAttribute("tiddler");
		var force = e.getAttribute("force");
		if(force != null || changeList == null || changeList.find(title) != null)
			{
			removeChildren(e);
			wikify(store.getTiddlerText(title,title),e,null,null);
			}
		}
};

function refreshElements(root,changeList)
{
	var nodes = root.childNodes;
	for(var c=0; c<nodes.length; c++)
		{
		var e = nodes[c],type;
		if(e.getAttribute)
			type = e.getAttribute("refresh");
		else
			type = null;
		var refresher = config.refreshers[type];
		if(refresher == undefined)
			{
			if(e.hasChildNodes())
				refreshElements(e,changeList);
			}
		else
			refresher(e,changeList);
		}
}

function applyHtmlMacros(root,tiddler)
{
	var e = root.firstChild;
	while(e)
		{
		var nextChild = e.nextSibling;
		if(e.getAttribute)
			{
			var macro = e.getAttribute("macro");
			if(macro)
				{
				var params = "";
				var p = macro.indexOf(" ");
				if(p != -1)
					{
					params = macro.substr(p+1);
					macro = macro.substr(0,p);
					}
				invokeMacro(e,macro,params,null,tiddler);
				}
			}
		if(e.hasChildNodes())
			applyHtmlMacros(e,tiddler);
		e = nextChild;
		}
}

function refreshPageTemplate(title)
{
	applyPageTemplate(title);
}

function applyPageTemplate(title)
{
	var stash = createTiddlyElement(document.body,"div");
	stash.style.display = "none";
	var display = document.getElementById("tiddlerDisplay");
	var nodes,t;
	if(display)
		{
		nodes = display.childNodes;
		for(t=nodes.length-1; t>=0; t--)
			stash.appendChild(nodes[t]);
		}
	var wrapper = document.getElementById("contentWrapper");
	if(!title)
		title = "PageTemplate";
	var html = store.getTiddlerText(title);
	wrapper.innerHTML = html;
	applyHtmlMacros(wrapper,null);
	refreshElements(wrapper,null);
	display = document.getElementById("tiddlerDisplay");
	removeChildren(display);
	if(!display)
		display = createTiddlyElement(wrapper,"div","tiddlerDisplay");
	nodes = stash.childNodes;
	for(t=nodes.length-1; t>=0; t--)
		display.appendChild(nodes[t]);
	stash.parentNode.removeChild(stash);
}

function refreshDisplay(hint)
{
	var e = document.getElementById("contentWrapper");
	refreshElements(e,hint == null ? null : [hint]);
}

function refreshPageTitle()
{
	document.title = wikifyPlain("SiteTitle") + " - " + wikifyPlain("SiteSubtitle");
}

function refreshStyles(title)
{
	setStylesheet(title == null ? "" : store.getRecursiveTiddlerText(title,"",10),title);
}

// ---------------------------------------------------------------------------------
// Options cookie stuff
// ---------------------------------------------------------------------------------

function loadOptionsCookie()
{
	if(safeMode)
		return;
	var cookies = document.cookie.split(";");
	for(var c=0; c<cookies.length; c++)
		{
		var p = cookies[c].indexOf("=");
		if(p != -1)
			{
			var name = cookies[c].substr(0,p).trim();
			var value = cookies[c].substr(p+1).trim();
			switch(name.substr(0,3))
				{
				case "txt":
					config.options[name] = unescape(value);
					break;
				case "chk":
					config.options[name] = value == "true";
					break;
				}
			}
		}
}

function saveOptionCookie(name)
{
	if(safeMode)
		return;
	var c = name + "=";
	switch(name.substr(0,3))
		{
		case "txt":
			c += escape(config.options[name].toString());
			break;
		case "chk":
			c += config.options[name] ? "true" : "false";
			break;
		}
	c += "; expires=Fri, 1 Jan 2038 12:00:00 UTC; path=/";
	document.cookie = c;
}

// ---------------------------------------------------------------------------------
// Saving
// ---------------------------------------------------------------------------------

var saveUsingSafari = false;
var startSaveArea = '<div id="' + 'storeArea">'; // Split up into two so that indexOf() of this source doesn't find it
var endSaveArea = '</d' + 'iv>';

// If there are unsaved changes, force the user to confirm before exitting
function confirmExit()
{
	hadConfirmExit = true;
	if(store && store.isDirty && store.isDirty())
		return config.messages.confirmExit;
}

// Give the user a chance to save changes before exitting
function checkUnsavedChanges()
{
	if(store && store.isDirty && store.isDirty() && window.hadConfirmExit === false)
		{
		if(confirm(config.messages.unsavedChangesWarning))
			saveChanges();
		}
}

// Save this tiddlywiki with the pending changes
function saveChanges()
{
	clearMessage();
	// Get the URL of the document
	var originalPath = document.location.toString();
	// Check we were loaded from a file URL
	if(originalPath.substr(0,5) != "file:")
		{
		alert(config.messages.notFileUrlError);
		if(store.tiddlerExists(config.messages.saveInstructions))
			displayTiddler(null,config.messages.saveInstructions);
		return;
		}
	var localPath = getLocalPath(originalPath);
	// Load the original file
	var original = loadFile(localPath);
	if(original == null)
		{
		alert(config.messages.cantSaveError);
		if(store.tiddlerExists(config.messages.saveInstructions))
			displayTiddler(null,config.messages.saveInstructions);
		return;
		}
	// Locate the storeArea div's
	var posOpeningDiv = original.indexOf(startSaveArea);
	var posClosingDiv = original.lastIndexOf(endSaveArea);
	if((posOpeningDiv == -1) || (posClosingDiv == -1))
		{
		alert(config.messages.invalidFileError.format([localPath]));
		return;
		}
	// Save the backup
	if(config.options.chkSaveBackups)
		{
		var backupPath = getBackupPath(localPath);
		var backup = saveFile(backupPath,original);
		if(backup)
			displayMessage(config.messages.backupSaved,"file://" + backupPath);
		else
			alert(config.messages.backupFailed);
		}
	// Save Rss
	if(config.options.chkGenerateAnRssFeed)
		{
		var rssPath = localPath.substr(0,localPath.lastIndexOf(".")) + ".xml";
		var rssSave = saveFile(rssPath,convertUnicodeToUTF8(generateRss()));
		if(rssSave)
			displayMessage(config.messages.rssSaved,"file://" + rssPath);
		else
			alert(config.messages.rssFailed);
		}
	// Save empty template
	if(config.options.chkSaveEmptyTemplate)
		{
		var emptyPath,p;
		if((p = localPath.lastIndexOf("/")) != -1)
			emptyPath = localPath.substr(0,p) + "/empty.html";
		else if((p = localPath.lastIndexOf("\\")) != -1)
			emptyPath = localPath.substr(0,p) + "\\empty.html";
		else
			emptyPath = localPath + ".empty.html";
		var empty = original.substr(0,posOpeningDiv + startSaveArea.length) + original.substr(posClosingDiv);
		var emptySave = saveFile(emptyPath,empty);
		if(emptySave)
			displayMessage(config.messages.emptySaved,"file://" + emptyPath);
		else
			alert(config.messages.emptyFailed);
		}
	// Save new file
	var revised = original.substr(0,posOpeningDiv + startSaveArea.length) +
				convertUnicodeToUTF8(allTiddlersAsHtml()) + "\n\t\t" +
				original.substr(posClosingDiv);
	var newSiteTitle = convertUnicodeToUTF8((wikifyPlain("SiteTitle") + " - " + wikifyPlain("SiteSubtitle")).htmlEncode());
	revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
	revised = revised.replaceChunk("<!--PRE-HEAD-START--"+">","<!--PRE-HEAD-END--"+">","\n" + store.getTiddlerText("MarkupPreHead","") + "\n");
	revised = revised.replaceChunk("<!--POST-HEAD-START--"+">","<!--POST-HEAD-END--"+">","\n" + store.getTiddlerText("MarkupPostHead","") + "\n");
	revised = revised.replaceChunk("<!--PRE-BODY-START--"+">","<!--PRE-BODY-END--"+">","\n" + store.getTiddlerText("MarkupPreBody","") + "\n");
	revised = revised.replaceChunk("<!--POST-BODY-START--"+">","<!--POST-BODY-END--"+">","\n" + store.getTiddlerText("MarkupPostBody","") + "\n");
	var save = saveFile(localPath,revised);
	if(save)
		{
		displayMessage(config.messages.mainSaved,"file://" + localPath);
		store.setDirty(false);
		}
	else
		alert(config.messages.mainFailed);
}

function getLocalPath(originalPath)
{
	// Remove any location or query part of the URL
	var argPos = originalPath.indexOf("?");
	if(argPos != -1)
		originalPath = originalPath.substr(0,argPos);
	var hashPos = originalPath.indexOf("#");
	if(hashPos != -1)
		originalPath = originalPath.substr(0,hashPos);
	// Convert file://localhost/ to file:///
	if(originalPath.indexOf("file://localhost/") == 0)
		originalPath = "file://" + originalPath.substr(16);
	// Convert to a native file format assuming
	// "file:///x:/path/path/path..." - pc local file --> "x:\path\path\path..."
	// "file://///server/share/path/path/path..." - FireFox pc network file --> "\\server\share\path\path\path..."
	// "file:///path/path/path..." - mac/unix local file --> "/path/path/path..."
	// "file://server/share/path/path/path..." - pc network file --> "\\server\share\path\path\path..."
	var localPath;
	if(originalPath.charAt(9) == ":") // pc local file
		localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\");
	else if(originalPath.indexOf("file://///") == 0) // FireFox pc network file
		localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\");
	else if(originalPath.indexOf("file:///") == 0) // mac/unix local file
		localPath = unescape(originalPath.substr(7));
	else if(originalPath.indexOf("file:/") == 0) // mac/unix local file
		localPath = unescape(originalPath.substr(5));
	else // pc network file
		localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\");
	return localPath;
}

function getBackupPath(localPath)
{
	var backSlash = true;
	var dirPathPos = localPath.lastIndexOf("\\");
	if(dirPathPos == -1)
		{
		dirPathPos = localPath.lastIndexOf("/");
		backSlash = false;
		}
	var backupFolder = config.options.txtBackupFolder;
	if(!backupFolder || backupFolder == "")
		backupFolder = ".";
	var backupPath = localPath.substr(0,dirPathPos) + (backSlash ? "\\" : "/") + backupFolder + localPath.substr(dirPathPos);
	backupPath = backupPath.substr(0,backupPath.lastIndexOf(".")) + "." + (new Date()).convertToYYYYMMDDHHMMSSMMM() + ".html";
	return backupPath;
}

function generateRss()
{
	var s = [];
	var d = new Date();
	var u = store.getTiddlerText("SiteUrl",null);
	// Assemble the header
	s.push("<" + "?xml version=\"1.0\"?" + ">");
	s.push("<rss version=\"2.0\">");
	s.push("<channel>");
	s.push("<title>" + wikifyPlain("SiteTitle").htmlEncode() + "</title>");
	if(u)
		s.push("<link>" + u.htmlEncode() + "</link>");
	s.push("<description>" + wikifyPlain("SiteSubtitle").htmlEncode() + "</description>");
	s.push("<language>en-us</language>");
	s.push("<copyright>Copyright " + d.getFullYear() + " " + config.options.txtUserName.htmlEncode() + "</copyright>");
	s.push("<pubDate>" + d.toGMTString() + "</pubDate>");
	s.push("<lastBuildDate>" + d.toGMTString() + "</lastBuildDate>");
	s.push("<docs>http://blogs.law.harvard.edu/tech/rss</docs>");
	s.push("<generator>TiddlyWiki " + version.major + "." + version.minor + "." + version.revision + "</generator>");
	// The body
	var tiddlers = store.getTiddlers("modified","excludeLists");
	var n = config.numRssItems > tiddlers.length ? 0 : tiddlers.length-config.numRssItems;
	for (var t=tiddlers.length-1; t>=n; t--)
		s.push(tiddlers[t].saveToRss(u));
	// And footer
	s.push("</channel>");
	s.push("</rss>");
	// Save it all
	return s.join("\n");
}

function allTiddlersAsHtml()
{
	var savedTiddlers = [];
	var tiddlers = store.getTiddlers("title");
	for (var t = 0; t < tiddlers.length; t++)
		savedTiddlers.push(tiddlers[t].saveToDiv());
	return savedTiddlers.join("\n");
}

// UTF-8 encoding rules:
// 0x0000 - 0x007F:	0xxxxxxx
// 0x0080 - 0x07FF:	110xxxxx 10xxxxxx
// 0x0800 - 0xFFFF:	1110xxxx 10xxxxxx 10xxxxxx

function convertUTF8ToUnicode(u)
{
	if(window.Components)
		return mozConvertUTF8ToUnicode(u);
	else
		return manualConvertUTF8ToUnicode(u);
}

function manualConvertUTF8ToUnicode(u)
{
	var s = "";
	var t = 0;
	var b1, b2, b3;
	while(t < u.length)
		{
		b1 = u.charCodeAt(t++);
		if(b1 < 0x80)
			s += String.fromCharCode(b1);
		else if(b1 < 0xE0)
			{
			b2 = u.charCodeAt(t++);
			s += String.fromCharCode(((b1 & 0x1F) << 6) | (b2 & 0x3F));
			}
		else
			{
			b2 = u.charCodeAt(t++);
			b3 = u.charCodeAt(t++);
			s += String.fromCharCode(((b1 & 0xF) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F));
			}
	}
	return(s);
}

function mozConvertUTF8ToUnicode(u)
{
	if (window.netscape == undefined)
		return manualConvertUTF8ToUnicode(u); // fallback
	try
		{
		netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
		}
	catch(e)
		{
		return manualConvertUTF8ToUnicode(u);
		} // fallback
	var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
	converter.charset = "UTF-8";
	var s = converter.ConvertToUnicode(u);
	var fin = converter.Finish();
	return (fin.length > 0) ? s+fin : s;
}

function convertUnicodeToUTF8(s)
{
	if(saveUsingSafari)
		return s;
	else if(window.Components)
		return mozConvertUnicodeToUTF8(s);
	else
		return manualConvertUnicodeToUTF8(s);
}

function manualConvertUnicodeToUTF8(s)
{
	var re = /[^\u0000-\u007F]/g ;
	return s.replace(re, function($0) {return("&#" + $0.charCodeAt(0).toString() + ";");})
}

function mozConvertUnicodeToUTF8(s)
{
	netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
	var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
	converter.charset = "UTF-8";
	var u = converter.ConvertFromUnicode(s);
	var fin = converter.Finish();
	if(fin.length > 0)
		return u + fin;
	else
		return u;
}

function saveFile(fileUrl, content)
{
	var r = null;
	if(saveUsingSafari)
		r = safariSaveFile(fileUrl, content);
	if((r == null) || (r == false))
		r = mozillaSaveFile(fileUrl, content);
	if((r == null) || (r == false))
		r = ieSaveFile(fileUrl, content);
	if((r == null) || (r == false))
		r = operaSaveFile(fileUrl, content);
	return(r);
}

function loadFile(fileUrl)
{
	var r = null;
	if(saveUsingSafari)
		r = safariLoadFile(fileUrl);
	if((r == null) || (r == false))
		r = mozillaLoadFile(fileUrl);
	if((r == null) || (r == false))
		r = ieLoadFile(fileUrl);
	if((r == null) || (r == false))
		r = operaLoadFile(fileUrl);
	return(r);
}

// Returns null if it can't do it, false if there's an error, true if it saved OK
function ieSaveFile(filePath, content)
{
	try
		{
		var fso = new ActiveXObject("Scripting.FileSystemObject");
		}
	catch(e)
		{
		//alert("Exception while attempting to save\n\n" + e.toString());
		return(null);
		}
	var file = fso.OpenTextFile(filePath,2,-1,0);
	file.Write(content);
	file.Close();
	return(true);
}

// Returns null if it can't do it, false if there's an error, or a string of the content if successful
function ieLoadFile(filePath)
{
	try
		{
		var fso = new ActiveXObject("Scripting.FileSystemObject");
		var file = fso.OpenTextFile(filePath,1);
		var content = file.ReadAll();
		file.Close();
		}
	catch(e)
		{
		//alert("Exception while attempting to load\n\n" + e.toString());
		return(null);
		}
	return(content);
}

// Returns null if it can't do it, false if there's an error, true if it saved OK
function mozillaSaveFile(filePath, content)
{
	if(window.Components)
		try
			{
			netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
			var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
			file.initWithPath(filePath);
			if (!file.exists())
				file.create(0, 0664);
			var out = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
			out.init(file, 0x20 | 0x02, 00004,null);
			out.write(content, content.length);
			out.flush();
			out.close();
			return(true);
			}
		catch(e)
			{
			//alert("Exception while attempting to save\n\n" + e);
			return(false);
			}
	return(null);
}

// Returns null if it can't do it, false if there's an error, or a string of the content if successful
function mozillaLoadFile(filePath)
{
	if(window.Components)
		try
			{
			netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
			var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
			file.initWithPath(filePath);
			if (!file.exists())
				return(null);
			var inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
			inputStream.init(file, 0x01, 00004, null);
			var sInputStream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
			sInputStream.init(inputStream);
			return(sInputStream.read(sInputStream.available()));
			}
		catch(e)
			{
			//alert("Exception while attempting to load\n\n" + e);
			return(false);
			}
	return(null);
}

function operaUrlToFilename(url)
{
	var f = "//localhost";
	if(url.indexOf(f) == 0)
		return url.substring(f.length);
	var i = url.indexOf(":");
	if(i > 0)
		return url.substring(i-1);
	return url;
}

function operaSaveFile(filePath, content)
{
	try
		{
		var s = new java.io.PrintStream(new java.io.FileOutputStream(operaUrlToFilename(filePath)));
		s.print(content);
		s.close();
		}
	catch(e)
		{
		if(window.opera)
			opera.postError(e);
		return null;
		}
	return true;
}

function operaLoadFile(filePath)
{
	var content = [];
	try
		{
		var r = new java.io.BufferedReader(new java.io.FileReader(operaUrlToFilename(filePath)));
		var line;
		while ((line = r.readLine()) != null)
			content.push(new String(line));
		r.close();
		}
	catch(e)
		{
		if(window.opera)
			opera.postError(e);
		return null;
		}
	return content.join("\n");
}

function safariFilenameToUrl(filename) {
	return ("file://" + filename);
}

function safariLoadFile(url)
{
	url = safariFilenameToUrl(url);
	var plugin = document.embeds["tiddlyWikiSafariSaver"];
	return plugin.readURL(url);
}

function safariSaveFile(url,content)
{
	url = safariFilenameToUrl(url);
	var plugin = document.embeds["tiddlyWikiSafariSaver"];
	return plugin.writeStringToURL(content,url);
}

// Lifted from http://developer.apple.com/internet/webcontent/detectplugins.html
function detectPlugin()
{
	var daPlugins = detectPlugin.arguments;
	var pluginFound = false;
	if (navigator.plugins && navigator.plugins.length > 0)
		{
		var pluginsArrayLength = navigator.plugins.length;
		for (var pluginsArrayCounter=0; pluginsArrayCounter < pluginsArrayLength; pluginsArrayCounter++ )
			{
			var numFound = 0;
			for(var namesCounter=0; namesCounter < daPlugins.length; namesCounter++)
				{
				if( (navigator.plugins[pluginsArrayCounter].name.indexOf(daPlugins[namesCounter]) >= 0) ||
						(navigator.plugins[pluginsArrayCounter].description.indexOf(daPlugins[namesCounter]) >= 0) )
					numFound++;
				}
			if(numFound == daPlugins.length)
				{
				pluginFound = true;
				break;
				}
			}
	}
	return pluginFound;
}

// ---------------------------------------------------------------------------------
// TiddlyWiki-specific utility functions
// ---------------------------------------------------------------------------------

function createTiddlyButton(theParent,theText,theTooltip,theAction,theClass,theId,theAccessKey)
{
	var theButton = document.createElement("a");
	theButton.className = "button";
	if(theAction)
		{
		theButton.onclick = theAction;
		theButton.setAttribute("href","javascript:;");
		}
	theButton.setAttribute("title",theTooltip);
	if(theText)
		theButton.appendChild(document.createTextNode(theText));
	if(theClass)
		theButton.className = theClass;
	if(theId)
		theButton.id = theId;
	if(theParent)
		theParent.appendChild(theButton);
	if(theAccessKey)
		theButton.setAttribute("accessKey",theAccessKey);
	return(theButton);
}

function createTiddlyLink(place,title,includeText,theClass)
{
	var text = includeText ? title : null;
	var btn = createTiddlyButton(place,text,null,onClickTiddlerLink,theClass);
	btn.setAttribute("refresh","link");
	btn.setAttribute("tiddlyLink",title);
	refreshTiddlyLink(btn,title);
	return(btn);
}

function refreshTiddlyLink(e,title)
{
	var subTitle, theClass = "tiddlyLink";
	var tiddler = store.fetchTiddler(title);
	if(tiddler)
		{
		subTitle = tiddler.getSubtitle();
		theClass += " tiddlyLinkExisting";
		}
	else
		{
		subTitle = config.messages.undefinedTiddlerToolTip.format([title]);
		theClass += " tiddlyLinkNonExisting";
		if(store.isShadowTiddler(title))
			{
			subTitle = config.messages.shadowedTiddlerToolTip.format([title]);
			theClass += " shadow";
			}
		else
			subTitle = config.messages.undefinedTiddlerToolTip.format([title]);
		}
	e.className = theClass;
	e.title = subTitle;
}

function createExternalLink(place,url)
{
	var theLink = document.createElement("a");
	theLink.className = "externalLink";
	theLink.href = url;
	theLink.title = config.messages.externalLinkTooltip.format([url]);
	if(config.options.chkOpenInNewWindow)
		theLink.target = "_blank";
	place.appendChild(theLink);
	return(theLink);
}

// Event handler for clicking on a tiddly link
function onClickTiddlerLink(e)
{
	if (!e) var e = window.event;
	var theTarget = resolveTarget(e);
	var theLink = theTarget;
	var title = null;
	do {
		title = theLink.getAttribute("tiddlyLink");
		theLink = theLink.parentNode;
	} while(title == null && theLink != null);
	if(title)
		{
		var toggling = e.metaKey || e.ctrlKey;
		if(config.options.chkToggleLinks)
			toggling = !toggling;
		var opening;
		if(toggling && document.getElementById("tiddler" + title))
			story.closeTiddler(title,true,e.shiftKey || e.altKey);
		else
			story.displayTiddler(theTarget,title,null,true,e.shiftKey || e.altKey);
		}
	clearMessage();
	return(false);
}

// Create a button for a tag with a popup listing all the tiddlers that it tags
function createTagButton(place,tag,excludeTiddler)
{
	var theTag = createTiddlyButton(place,tag,config.views.wikified.tag.tooltip.format([tag]),onClickTag);
	theTag.setAttribute("tag",tag);
	if(excludeTiddler)
		theTag.setAttribute("tiddler",excludeTiddler);
	return(theTag);
}

// Event handler for clicking on a tiddler tag
function onClickTag(e)
{
	if (!e) var e = window.event;
	var theTarget = resolveTarget(e);
	var popup = Popup.create(this);
	var tag = this.getAttribute("tag");
	var title = this.getAttribute("tiddler");
	if(popup && tag)
		{
		var tagged = store.getTaggedTiddlers(tag);
		var titles = [];
		var li,r;
		for(r=0;r<tagged.length;r++)
			if(tagged[r].title != title)
				titles.push(tagged[r].title);
		var lingo = config.views.wikified.tag;
		if(titles.length > 0)
			{
			var openAll = createTiddlyButton(createTiddlyElement(popup,"li"),lingo.openAllText.format([tag]),lingo.openAllTooltip,onClickTagOpenAll);
			openAll.setAttribute("tag",tag);
			createTiddlyElement(createTiddlyElement(popup,"li"),"hr");
			for(r=0; r<titles.length; r++)
				{
				createTiddlyLink(createTiddlyElement(popup,"li"),titles[r],true);
				}
			}
		else
			createTiddlyText(createTiddlyElement(popup,"li",null,"disabled"),lingo.popupNone.format([tag]));
		createTiddlyElement(createTiddlyElement(popup,"li"),"hr");
		var h = createTiddlyLink(createTiddlyElement(popup,"li"),tag,false);
		createTiddlyText(h,lingo.openTag.format([tag]));
		}
	Popup.show(popup,false);
	e.cancelBubble = true;
	if (e.stopPropagation) e.stopPropagation();
	return(false);
}

// Event handler for 'open all' on a tiddler popup
function onClickTagOpenAll(e)
{
	if (!e) var e = window.event;
	var tag = this.getAttribute("tag");
	var tagged = store.getTaggedTiddlers(tag);
	for(var t=tagged.length-1; t>=0; t--)
		story.displayTiddler(this,tagged[t].title,null,false,e.shiftKey || e.altKey);
	return(false);
}

function onClickError(e)
{
	if (!e) var e = window.event;
	var popup = Popup.create(this);
	var lines = this.getAttribute("errorText").split("\n");
	for(var t=0; t<lines.length; t++)
		createTiddlyElement(popup,"li",null,null,lines[t]);
	Popup.show(popup,false);
	e.cancelBubble = true;
	if (e.stopPropagation) e.stopPropagation();
	return false;
}

function createTiddlyError(place,title,text)
{
	var btn = createTiddlyButton(place,title,null,onClickError,"errorButton");
	if (text) btn.setAttribute("errorText",text);
}

// ---------------------------------------------------------------------------------
// Animation engine
// ---------------------------------------------------------------------------------

function Animator()
{
	this.running = 0; // Incremented at start of each animation, decremented afterwards. If zero, the interval timer is disabled
	this.timerID; // ID of the timer used for animating
	this.animations = []; // List of animations in progress
	return this;
}

// Start animation engine
Animator.prototype.startAnimating = function() // Variable number of arguments
{
	for(var t=0; t<arguments.length; t++)
		this.animations.push(arguments[t]);
	if(this.running == 0)
		{
		var me = this;
		this.timerID = window.setInterval(function() {me.doAnimate(me);},5);
		}
	this.running += arguments.length;
}

// Perform an animation engine tick, calling each of the known animation modules
Animator.prototype.doAnimate = function(me)
{
	var a = 0;
	while(a < me.animations.length)
		{
		var animation = me.animations[a];
		if(animation.tick())
			a++;
		else
			{
			me.animations.splice(a,1);
			if(--me.running == 0)
				window.clearInterval(me.timerID);
			}
		}
}

// Map a 0..1 value to 0..1, but slow down at the start and end
Animator.slowInSlowOut = function(progress)
{
	return(1-((Math.cos(progress * Math.PI)+1)/2));
}

// ---------------------------------------------------------------------------------
// Cascade animation
// ---------------------------------------------------------------------------------

function Cascade(text,startElement,targetElement,slowly)
{
	var winWidth = findWindowWidth();
	var winHeight = findWindowHeight();
	this.elements = [];
	this.startElement = startElement;
	this.startLeft = findPosX(this.startElement);
	this.startTop = findPosY(this.startElement);
	this.startWidth = Math.min(this.startElement.offsetWidth,winWidth);
	this.startHeight = Math.min(this.startElement.offsetHeight,winHeight);
	this.targetElement = targetElement;
	targetElement.style.position = "relative";
	targetElement.style.zIndex = 2;
	this.targetLeft = findPosX(this.targetElement);
	this.targetTop = findPosY(this.targetElement);
	this.targetWidth = Math.min(this.targetElement.offsetWidth,winWidth);
	this.targetHeight = Math.min(this.targetElement.offsetHeight,winHeight);
	this.progress = -1;
	this.steps = slowly ? config.cascadeSlow : config.cascadeFast;
	this.text = text;
	this.tick();
	return this;
}

Cascade.prototype.tick = function()
{
	this.progress++;
	if(this.progress >= this.steps)
		{
		while(this.elements.length > 0)
			this.removeTail();
		this.targetElement.style.position = "static";
		this.targetElement.style.zIndex = null;
		return false;
		}
	else
		{
		if(this.elements.length > 0 && this.progress > config.cascadeDepth)
			this.removeTail();
		if(this.progress < (this.steps - config.cascadeDepth))
			{
			var f = Animator.slowInSlowOut(this.progress/(this.steps - config.cascadeDepth - 1));
			var e = createTiddlyElement(document.body,"div",null,"cascade",this.text);
			e.style.zIndex = 1;
			e.style.left = this.startLeft + (this.targetLeft-this.startLeft) * f + "px";
			e.style.top = this.startTop + (this.targetTop-this.startTop) * f + "px";
			e.style.width = this.startWidth + (this.targetWidth-this.startWidth) * f + "px";
			e.style.height = this.startHeight + (this.targetHeight-this.startHeight) * f + "px";
			e.style.display = "block";
			this.elements.push(e);
			}
		return true;
		}
}

Cascade.prototype.removeTail = function()
{
	var e = this.elements[0];
	e.parentNode.removeChild(e);
	this.elements.shift();
}

// ---------------------------------------------------------------------------------
// Scroller animation
// ---------------------------------------------------------------------------------

function Scroller(targetElement,slowly)
{
	this.targetElement = targetElement;
	this.startScroll = findScrollY();
	this.targetScroll = ensureVisible(targetElement);
	this.progress = 0;
	this.step = slowly ? config.animSlow : config.animFast;
	return this;
}

Scroller.prototype.tick = function()
{
	this.progress += this.step;
	if(this.progress > 1)
		{
		window.scrollTo(0,this.targetScroll);
		return false;
		}
	else
		{
		var f = Animator.slowInSlowOut(this.progress);
		window.scrollTo(0,this.startScroll + (this.targetScroll-this.startScroll) * f);
		return true;
		}
}

// ---------------------------------------------------------------------------------
// Slider animation
// ---------------------------------------------------------------------------------

// deleteMode - "none", "all" [delete target element and it's children], [only] "children" [but not the target element]
function Slider(element,opening,slowly,deleteMode)
{
	this.element = element;
	element.style.display = "block";
	this.deleteMode = deleteMode;
	this.element.style.height = "auto";
	this.realHeight = element.offsetHeight;
	this.opening = opening;
	this.step = slowly ? config.animSlow : config.animFast;
	if(opening)
		{
		this.progress = 0;
		element.style.height = "0px";
		element.style.display = "block";
		}
	else
		{
		this.progress = 1;
		this.step = -this.step;
		}
	element.style.overflow = "hidden";
	return this;
}

Slider.prototype.stop = function()
{
	if(this.opening)
		{
		this.element.style.height = "auto";
		this.element.style.opacity = 1;
		this.element.style.filter = "alpha(opacity:100)";
		}
	else
		{
		switch(this.deleteMode)
			{
			case "none":
				this.element.style.display = "none";
				break;
			case "all":
				this.element.parentNode.removeChild(this.element);
				break;
			case "children":
				removeChildren(this.element);
				break;
			}
		}
}

Slider.prototype.tick = function()
{
	this.progress += this.step;
	if(this.progress < 0 || this.progress > 1)
		{
		this.stop();
		return false;
		}
	else
		{
		var f = Animator.slowInSlowOut(this.progress);
		var h = this.realHeight * f;
		this.element.style.height = h + "px";
		this.element.style.opacity = f;
		this.element.style.filter = "alpha(opacity:" + f * 100 +")";
		return true;
		}
}

// ---------------------------------------------------------------------------------
// Popup menu
// ---------------------------------------------------------------------------------

var Popup = {
	stack: [] // Array of objects with members root: and popup:
	};

Popup.create = function(root)
{
	Popup.remove();
	var popup = createTiddlyElement(document.body,"ol","popup","popup",null);
	Popup.stack.push({root: root, popup: popup});
	return popup;
}

Popup.onDocumentClick = function(e)
{
	if (!e) var e = window.event;
	var target = resolveTarget(e);
	if(e.eventPhase == undefined)
		Popup.remove();
	else if(e.eventPhase == Event.BUBBLING_PHASE || e.eventPhase == Event.AT_TARGET)
		Popup.remove();
	return true;
}

Popup.show = function(unused,slowly)
{
	var curr = Popup.stack[Popup.stack.length-1];
	var rootLeft = findPosX(curr.root);
	var rootTop = findPosY(curr.root);
	var rootHeight = curr.root.offsetHeight;
	var popupLeft = rootLeft;
	var popupTop = rootTop + rootHeight;
	var popupWidth = curr.popup.offsetWidth;
	var winWidth = findWindowWidth();
	if(popupLeft + popupWidth > winWidth)
		popupLeft = winWidth - popupWidth;
	curr.popup.style.left = popupLeft + "px";
	curr.popup.style.top = popupTop + "px";
	curr.popup.style.display = "block";
	addClass(curr.root,"highlight");
	if(config.options.chkAnimate)
		anim.startAnimating(new Scroller(curr.popup,slowly));
	else
		window.scrollTo(0,ensureVisible(curr.popup));
}

Popup.remove = function()
{
	if(Popup.stack.length > 0)
		{
		Popup.removeFrom(0);
		}
}

Popup.removeFrom = function(from)
{
	for(var t=Popup.stack.length-1; t>=from; t--)
		{
		var p = Popup.stack[t];
		removeClass(p.root,"highlight");
		p.popup.parentNode.removeChild(p.popup);
		}
	Popup.stack = Popup.stack.slice(0,from);
}

// Backwards compatibility
var createTiddlerPopup = Popup.create;
var scrollToTiddlerPopup = Popup.show;
var hideTiddlerPopup = Popup.remove;

// ---------------------------------------------------------------------------------
// Augmented methods for the JavaScript Number(), Array() and String() objects
// ---------------------------------------------------------------------------------

// Clamp a number to a range
Number.prototype.clamp = function(min,max)
{
	var c = this;
	if(c < min)
		c = min;
	if(c > max)
		c = max;
	return c;
}

// Find an entry in an array. Returns the array index or null
Array.prototype.find = function(item)
{
	for(var t=0; t<this.length; t++)
		if(this[t] == item)
			return t;
	return null;
}

// Return whether an entry exists in an array
Array.prototype.contains = function(item)
{
    return this.find(item) != null;
};

// Return whether one of a list of values exists in an array
Array.prototype.containsAny = function(items)
{
    for(var i=0; i<items.length; i++)
        if (this.contains(items[i]))
            return true;
    return false;
};

// Return wheter all of a list of values exists in an array
Array.prototype.containsAll = function(items)
{
    for (var i = 0; i<items.length; i++)
        if (!this.contains(items[i]))
            return false;
    return true;
};

// Push a new value into an array only if it is not already present in the array. If the optional unique parameter is false, it reverts to a normal push
Array.prototype.pushUnique = function(item,unique)
{
	if(unique != undefined && unique == false)
		this.push(item);
	else
		{
		if(this.find(item) == null)
			this.push(item);
		}
}

// Get characters from the right end of a string
String.prototype.right = function(n)
{
	if(n < this.length)
		return this.slice(this.length-n);
	else
		return this;
}

// Trim whitespace from both ends of a string
String.prototype.trim = function()
{
	return this.replace(/^\s*|\s*$/g,"");
}

// Convert a string from a CSS style property name to a JavaScript style name ("background-color" -> "backgroundColor")
String.prototype.unDash = function()
{
	var s = this.split("-");
	if(s.length > 1)
		for(var t=1; t<s.length; t++)
			s[t] = s[t].substr(0,1).toUpperCase() + s[t].substr(1);
	return s.join("");
}

// Substitute substrings from an array into a format string that includes '%1'-type specifiers
String.prototype.format = function(substrings)
{
	var subRegExp = new RegExp("(?:%(\\d+))","mg");
	var currPos = 0;
	var r = [];
	do {
		var match = subRegExp.exec(this);
		if(match && match[1])
			{
			if(match.index > currPos)
				r.push(this.substring(currPos,match.index));
			r.push(substrings[parseInt(match[1])]);
			currPos = subRegExp.lastIndex;
			}
	} while(match);
	if(currPos < this.length)
		r.push(this.substring(currPos,this.length));
	return r.join("");
}

// Escape any special RegExp characters with that character preceded by a backslash
String.prototype.escapeRegExp = function()
{
	var s = "\\^$*+?()=!|,{}[].";
	var c = this;
	for(var t=0; t<s.length; t++)
		c = c.replace(new RegExp("\\" + s.substr(t,1),"g"),"\\" + s.substr(t,1));
	return c;
}

// Convert & to "&amp;", < to "&lt;", > to "&gt;" and " to "&quot;"
String.prototype.htmlEncode = function()
{
	var regexpAmp = new RegExp("&","mg");
	var regexpLessThan = new RegExp("<","mg");
	var regexpGreaterThan = new RegExp(">","mg");
	var regexpQuote = new RegExp("\"","mg");
	return(this.replace(regexpAmp,"&amp;").replace(regexpLessThan,"&lt;").replace(regexpGreaterThan,"&gt;").replace(regexpQuote,"&quot;"));
}

// Convert "&amp;" to &, "&lt;" to <, "&gt;" to > and "&quot;" to "
String.prototype.htmlDecode = function()
{
	var regexpAmp = new RegExp("&amp;","mg");
	var regexpLessThan = new RegExp("&lt;","mg");
	var regexpGreaterThan = new RegExp("&gt;","mg");
	var regexpQuote = new RegExp("&quot;","mg");
	return(this.replace(regexpLessThan,"<").replace(regexpGreaterThan,">").replace(regexpQuote,"\"").replace(regexpAmp,"&"));
}

// Parse a space-separated string of name:value parameters where:
//   - the name or the value can be optional (and a separate default is used instead)
//     - in case of ambiguity, a lone word is taken to be a value
//   - if both the name and value are present they must be separated by a colon
//   - the name and the value may both be quoted with single- or double-quotes, double-square brackets
//   - names or values quoted with {{double-curly braces}} are evaluated as a JavaScript expression
// The result is an array of objects:
//   result[0] = object with a member for each parameter name, value of that member being an array of values
//   result[1..n] = one object for each parameter, with 'name' and 'value' members
String.prototype.parseParams = function(defaultName,defaultValue,allowEval,noNames)
{
	var parseToken = function(match,p)
		{
		var n;
		if(match[p]) // Double quoted
			n = match[p];
		else if(match[p+1]) // Single quoted
			n = match[p+1];
		else if(match[p+2]) // Double-square-bracket quoted
			n = match[p+2];
		else if(match[p+3]) // Double-brace quoted
			try
				{
				n = match[p+3];
				if(allowEval)
					n = window.eval(n);
				}
			catch(ex)
				{
				throw "Unable to evaluate {{" + match[p+3] + "}}: " + ex.toString();
				}
		else if(match[p+4]) // Unquoted
			n = match[p+4];
		return n;
		};
	var r = [{}];
	var dblQuote = "(?:\"((?:(?:\\\\\")|[^\"])*)\")";
	var sngQuote = "(?:'((?:(?:\\\\\')|[^'])*)')";
	var dblSquare = "(?:\\[\\[((?:\\s|\\S)*?)\\]\\])";
	var dblBrace = "(?:\\{\\{((?:\\s|\\S)*?)\\}\\})";
	var unQuoted = noNames ? "([^\"'\\s]\\S*)" : "([^\"':\\s][^\\s:]*)";
	var skipSpace = "(?:\\s*)";
	var token = "(?:" + dblQuote + "|" + sngQuote + "|" + dblSquare + "|" + dblBrace + "|" + unQuoted + ")";
	var re = noNames
		? new RegExp(token,"mg")
		: new RegExp(skipSpace + token + skipSpace + "(?:(\\:)" + skipSpace + token + "){0,1}","mg");
	var params = [];
	do {
		var match = re.exec(this);
		if(match)
			{
			var n = parseToken(match,1);
			if(noNames)
				r.push({name: "", value: n});
			else
				{
				var v = parseToken(match,7);
				if(v == null && defaultName)
					{
					v = n;
					n = defaultName;
					}
				else if(v == null && defaultValue)
					v = defaultValue;
				r.push({name: n, value: v});
				}
			}
	} while(match);
	// Summarise parameters into first element
	for(var t=1; t<r.length; t++)
		{
		if(r[0][r[t].name])
			r[0][r[t].name].push(r[t].value);
		else
			r[0][r[t].name] = [r[t].value];
		}
	return r;
}

// Process a string list of macro parameters into an array. Parameters can be quoted with "", '',
// [[]], {{ }} or left unquoted (and therefore space-separated). Double-braces {{}} results in
// an *evaluated* parameter: e.g. {{config.options.txtUserName}} results in the current user's name.
String.prototype.readMacroParams = function()
{
	var p = this.parseParams("list",null,true,true);
	var n = [];
	for(var t=1; t<p.length; t++)
		n.push(p[t].value);
	return n;
}

// Process a string list of unique tiddler names into an array. Tiddler names that have spaces in them must be [[bracketed]]
String.prototype.readBracketedList = function(unique)
{
	var p = this.parseParams("list",null,false,true);
	var n = [];
	for(var t=1; t<p.length; t++)
		n.pushUnique(p[t].value,unique);
	return n;
}

// Replace a chunk of a string given start and end markers
String.prototype.replaceChunk = function(start,end,sub)
{
	var s = this.indexOf(start);
	if(s != -1)
		{
		var e = this.indexOf(end,s + start.length);
		if(e != -1)
			return this.substring(0,s + start.length) + sub + this.substring(e);
		}
	return this;
}

// Static method to bracket a string with double square brackets if it contains a space
String.encodeTiddlyLink = function(title)
{
	if(title.indexOf(" ") == -1)
		return(title);
	else
		return("[[" + title + "]]");
}

// Static method to left-pad a string with 0s to a certain width
String.zeroPad = function(n,d)
{
	var s = n.toString();
	if(s.length < d)
		s = "000000000000000000000000000".substr(0,d-s.length) + s;
	return(s);
}

// ---------------------------------------------------------------------------------
// RGB colour object
// ---------------------------------------------------------------------------------

// Construct an RGB colour object from a '#rrggbb', '#rgb' or 'rgb(n,n,n)' string or from separate r,g,b values
function RGB(r,g,b)
{
	this.r = 0;
	this.g = 0;
	this.b = 0;
	if(typeof r == "string")
		{
		if(r.substr(0,1) == "#")
			{
			if(r.length == 7)
				{
				this.r = parseInt(r.substr(1,2),16)/255;
				this.g = parseInt(r.substr(3,2),16)/255;
				this.b = parseInt(r.substr(5,2),16)/255;
				}
			else
				{
				this.r = parseInt(r.substr(1,1),16)/15;
				this.g = parseInt(r.substr(2,1),16)/15;
				this.b = parseInt(r.substr(3,1),16)/15;
				}
			}
		else
			{
			var rgbPattern = /rgb\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/ ;
			var c = r.match(rgbPattern);
			if (c)
				{
				this.r = parseInt(c[1],10)/255;
				this.g = parseInt(c[2],10)/255;
				this.b = parseInt(c[3],10)/255;
				}
			}
		}
	else
		{
		this.r = r;
		this.g = g;
		this.b = b;
		}
	return this;
}

// Mixes this colour with another in a specified proportion
// c = other colour to mix
// f = 0..1 where 0 is this colour and 1 is the new colour
// Returns an RGB object
RGB.prototype.mix = function(c,f)
{
	return new RGB(this.r + (c.r-this.r) * f,this.g + (c.g-this.g) * f,this.b + (c.b-this.b) * f);
}

// Return an rgb colour as a #rrggbb format hex string
RGB.prototype.toString = function()
{
	var r = this.r.clamp(0,1);
	var g = this.g.clamp(0,1);
	var b = this.b.clamp(0,1);
	return("#" + ("0" + Math.floor(r * 255).toString(16)).right(2) +
				 ("0" + Math.floor(g * 255).toString(16)).right(2) +
				 ("0" + Math.floor(b * 255).toString(16)).right(2));
}

// ---------------------------------------------------------------------------------
// Augmented methods for the JavaScript Date() object
// ---------------------------------------------------------------------------------

// Substitute date components into a string
Date.prototype.formatString = function(template)
{
	template = template.replace(/YYYY/g,this.getFullYear());
	template = template.replace(/YY/g,String.zeroPad(this.getFullYear()-2000,2));
	template = template.replace(/MMM/g,config.messages.dates.months[this.getMonth()]);
	template = template.replace(/0MM/g,String.zeroPad(this.getMonth()+1,2));
	template = template.replace(/MM/g,this.getMonth()+1);
	template = template.replace(/DDD/g,config.messages.dates.days[this.getDay()]);
	template = template.replace(/0DD/g,String.zeroPad(this.getDate(),2));
	template = template.replace(/DDth/g,this.getDate()+this.daySuffix());
	template = template.replace(/DD/g,this.getDate());
	template = template.replace(/0hh/g,String.zeroPad(this.getHours(),2));
	template = template.replace(/hh/g,this.getHours());
	template = template.replace(/0mm/g,String.zeroPad(this.getMinutes(),2));
	template = template.replace(/mm/g,this.getMinutes());
	template = template.replace(/0ss/g,String.zeroPad(this.getSeconds(),2));
	template = template.replace(/ss/g,this.getSeconds());
	return template;
}

Date.prototype.daySuffix = function()
{
	var num = this.getDate();
	if (num >= 11 && num <= 13) return "th";
	else if (num.toString().substr(-1)=="1") return "st";
	else if (num.toString().substr(-1)=="2") return "nd";
	else if (num.toString().substr(-1)=="3") return "rd";
	return "th";
}

// Convert a date to local YYYYMMDDHHMM string format
Date.prototype.convertToLocalYYYYMMDDHHMM = function()
{
	return(String.zeroPad(this.getFullYear(),4) + String.zeroPad(this.getMonth()+1,2) + String.zeroPad(this.getDate(),2) + String.zeroPad(this.getHours(),2) + String.zeroPad(this.getMinutes(),2));
}

// Convert a date to UTC YYYYMMDDHHMM string format
Date.prototype.convertToYYYYMMDDHHMM = function()
{
	return(String.zeroPad(this.getUTCFullYear(),4) + String.zeroPad(this.getUTCMonth()+1,2) + String.zeroPad(this.getUTCDate(),2) + String.zeroPad(this.getUTCHours(),2) + String.zeroPad(this.getUTCMinutes(),2));
}

// Convert a date to UTC YYYYMMDD.HHMMSSMMM string format
Date.prototype.convertToYYYYMMDDHHMMSSMMM = function()
{
	return(String.zeroPad(this.getUTCFullYear(),4) + String.zeroPad(this.getUTCMonth()+1,2) + String.zeroPad(this.getUTCDate(),2) + "." + String.zeroPad(this.getUTCHours(),2) + String.zeroPad(this.getUTCMinutes(),2) + String.zeroPad(this.getUTCSeconds(),2) + String.zeroPad(this.getUTCMilliseconds(),4));
}

// Static method to create a date from a UTC YYYYMMDDHHMM format string
Date.convertFromYYYYMMDDHHMM = function(d)
{
	var theDate = new Date(Date.UTC(parseInt(d.substr(0,4),10),
							parseInt(d.substr(4,2),10)-1,
							parseInt(d.substr(6,2),10),
							parseInt(d.substr(8,2),10),
							parseInt(d.substr(10,2),10),0,0));
	return(theDate);
}

// ---------------------------------------------------------------------------------
// DOM utilities - many derived from www.quirksmode.org
// ---------------------------------------------------------------------------------

function drawGradient(place,horiz,colours)
{
	for(var t=0; t<= 100; t+=2)
		{
		var bar = document.createElement("div");
		place.appendChild(bar);
		bar.style.position = "absolute";
		bar.style.left = horiz ? t + "%" : 0;
		bar.style.top = horiz ? 0 : t + "%";
		bar.style.width = horiz ? (101-t) + "%" : "100%";
		bar.style.height = horiz ? "100%" : (101-t) + "%";
		bar.style.zIndex = -1;
		var f = t/100;
		var p = f*(colours.length-1);
		bar.style.backgroundColor = colours[Math.floor(p)].mix(colours[Math.ceil(p)],p-Math.floor(p)).toString();
		}
}

function createTiddlyText(theParent,theText)
{
	return theParent.appendChild(document.createTextNode(theText));
}

function createTiddlyElement(theParent,theElement,theID,theClass,theText)
{
	var e = document.createElement(theElement);
	if(theClass != null)
		e.className = theClass;
	if(theID != null)
		e.setAttribute("id",theID);
	if(theText != null)
		e.appendChild(document.createTextNode(theText));
	if(theParent != null)
		theParent.appendChild(e);
	return(e);
}

// Add an event handler
// Thanks to John Resig, via QuirksMode
function addEvent(obj,type,fn)
{
	if(obj.attachEvent)
		{
		obj['e'+type+fn] = fn;
		obj[type+fn] = function(){obj['e'+type+fn](window.event);}
		obj.attachEvent('on'+type,obj[type+fn]);
		}
	else
		obj.addEventListener(type,fn,false);
}

// Remove  an event handler
// Thanks to John Resig, via QuirksMode
function removeEvent(obj,type,fn)
{
	if(obj.detachEvent)
		{
		obj.detachEvent('on'+type,obj[type+fn]);
		obj[type+fn] = null;
		}
	else
		obj.removeEventListener(type,fn,false);
}

function addClass(e,theClass)
{
	removeClass(e,theClass);
	e.className += " " + theClass;
}

function removeClass(e,theClass)
{
	var newClass = [];
	var currClass = e.className.split(" ");
	for(var t=0; t<currClass.length; t++)
		if(currClass[t] != theClass)
			newClass.push(currClass[t]);
	e.className = newClass.join(" ");
}

function hasClass(e,theClass)
{
	var c = e.className;
	if(c)
		{
		c = c.split(" ");
		for(var t=0; t<c.length; t++)
			if(c[t] == theClass)
				return true;
		}
	return false;
}

// Resolve the target object of an event
function resolveTarget(e)
{
	var obj;
	if (e.target)
		obj = e.target;
	else if (e.srcElement)
		obj = e.srcElement;
	if (obj.nodeType == 3) // defeat Safari bug
		obj = obj.parentNode;
	return(obj);
}

// Return the content of an element as plain text with no formatting
function getPlainText(e)
{
	var text = "";
	if(e.innerText)
		text = e.innerText;
	else if(e.textContent)
		text = e.textContent;
	return text;
}

// Get the scroll position for window.scrollTo necessary to scroll a given element into view
function ensureVisible(e)
{
	var posTop = findPosY(e);
	var posBot = posTop + e.offsetHeight;
	var winTop = findScrollY();
	var winHeight = findWindowHeight();
	var winBot = winTop + winHeight;
	if(posTop < winTop)
		return(posTop);
	else if(posBot > winBot)
		{
		if(e.offsetHeight < winHeight)
			return(posTop - (winHeight - e.offsetHeight));
		else
			return(posTop);
		}
	else
		return(winTop);
}

// Get the current width of the display window
function findWindowWidth()
{
	return(window.innerWidth ? window.innerWidth : document.documentElement.clientWidth);
}

// Get the current height of the display window
function findWindowHeight()
{
	return(window.innerHeight ? window.innerHeight : document.documentElement.clientHeight);
}

// Get the current horizontal page scroll position
function findScrollX()
{
	return(window.scrollX ? window.scrollX : document.documentElement.scrollLeft);
}

// Get the current vertical page scroll position
function findScrollY()
{
	return(window.scrollY ? window.scrollY : document.documentElement.scrollTop);
}

function findPosX(obj)
{
	var curleft = 0;
	while (obj.offsetParent)
		{
		curleft += obj.offsetLeft;
		obj = obj.offsetParent;
		}
	return curleft;
}

function findPosY(obj)
{
	var curtop = 0;
	while (obj.offsetParent)
		{
		curtop += obj.offsetTop;
		obj = obj.offsetParent;
		}
	return curtop;
}

// Blur a particular element
function blurElement(e)
{
	if(e != null && e.focus && e.blur)
		{
		e.focus();
		e.blur();
		}
}

// Create a non-breaking space
function insertSpacer(place)
{
	var e = document.createTextNode(String.fromCharCode(160));
	if(place)
		place.appendChild(e);
	return e;
}

// Remove all children of a node
function removeChildren(e)
{
	while(e.hasChildNodes())
		e.removeChild(e.firstChild);
}

// Add a stylesheet, replacing any previous custom stylesheet
function setStylesheet(s,id)
{
	if(!id)
		id = "customStyleSheet";
	var n = document.getElementById(id);
	if(document.createStyleSheet) // Test for IE's non-standard createStyleSheet method
		{
		if(n)
			n.parentNode.removeChild(n);
		// This failed without the &nbsp;
		document.getElementsByTagName("head")[0].insertAdjacentHTML("beforeEnd","&nbsp;<style id='" + id + "'>" + s + "</style>");
		}
	else
		{
		if(n)
			n.replaceChild(document.createTextNode(s),n.firstChild);
		else
			{
			var n = document.createElement("style");
			n.type = "text/css";
			n.id = id;
			n.appendChild(document.createTextNode(s));
			document.getElementsByTagName("head")[0].appendChild(n);
			}
		}
}

// ---------------------------------------------------------------------------------
// End of scripts
// ---------------------------------------------------------------------------------

</script>
<style type="text/css">

#saveTest {
	display: none;
}

.zoomer {
	display: none;
}

#messageArea {
	display: none;
}

#storeArea, #copyright {
	display: none;
}

.popup {
	position: absolute;
}

</style>
<noscript>
<style type="text/css">

#storeArea {
	display: block;
	margin: 4em 10em 3em;
}

#storeArea div {
 padding: 0.5em;
 margin: 1em 0em 0em 0em;
 border-color: #f0f0f0 #606060 #404040 #d0d0d0;
 border-style: solid;
 border-width: 2px;
 overflow: auto;
}

#javascriptWarning {
	width: 100%;
	text-align: center;
	font-weight: bold;
	background-color: #dd1100;
	color: #fff;
	padding:1em 0em;
}

</style>

</noscript>
<!--POST-HEAD-START-->

<!--POST-HEAD-END-->
</head>
<body class="twShell" onload="main();" onunload="if(window.checkUnsavedChanges) checkUnsavedChanges();" onbeforeunload="if(window.confirmExit) return confirmExit();">
<!--PRE-BODY-START-->

<!--PRE-BODY-END-->
	<script>
	if (detectPlugin("TiddlyWiki Saver"))
		{
		document.write('<embed style="display: none" name="tiddlyWikiSafariSaver" width="0" height="0" type="application/x-webkit-tiddlywiki-saver"></embed>');
		saveUsingSafari = true;
		}
	</script>
	<div id="copyright">
	Welcome to TiddlyWiki by Jeremy Ruston, Copyright &copy; 2005 Osmosoft Limited
	</div>

	<noscript>
		<div id="javascriptWarning">This page requires JavaScript to function properly</div>
	</noscript>
	<div id="saveTest"></div>
	<div id="contentWrapper"></div>
	<div id="contentStash"></div>
	<div id="storeArea"><div tiddler="Aggregated links" modifier="ashinw" modified="200608051928" created="200606012102" tags="Main">!!Frequented sites\n&lt;&lt;tiddler 'Frequented sites'&gt;&gt;</div>
<div tiddler="Background - Look around one last time before you jump" modifier="ashinw" modified="200608042003" created="200608041859" tags="Primer Background">!Background - Look around one last time before you jump\nThe Google Widget Toolkit (GWT) is very true to its name. It is more than a base framework, it is more than a widget library, it is more than a hosted Javascript emulation platform, and it is more than a compiler. These aggregates and the way they were engineered together is what kept this 10 year old Java veteran from jumping off to Ruby for his current development. Ok, so how could someone get so excited about a toolkit. Well perhaps I have been severely scarred from my AJAX battles with the Dojo toolkit and other such offerings. Prior to discovering GWT I attempted to port a large Wiki/SVN/DAV server to a Dojo approach. No disrespect to the Dojo toolkit intended, it is likely the best toolkit that could be developed with the pure javascript constraint. However, after two weeks I felt deeply sorry for todays young software engineers. The technology stack was ..., well, lets just say I felt like I had regressed beyond 17 years of software development and still delivered bugger all business value. Something had to change...\n</div>
<div tiddler="Behind the scenes" modifier="ashinw" modified="200608051803" created="200606012100" tags="Main">![[Whats in this release]]\n![[GWT primer for Gem'ers]]\n![[Gems-GWT-User project roadmap]]\n![[Why another distribution]]</div>
<div tiddler="Build baseline Gwt-user distribution" modifier="ashinw" modified="200608040313" created="200608040259" tags="Task Done [[Gems-GWT-User project roadmap]]">* create documentation\n* commit baseline to trunk\n* tag baseline as 1.0.21\n* issue communication to:\n** GWT group\n** GEMS group</div>
<div tiddler="Calendar" modifier="AshinWima" modified="200606011744" created="200603090216" tags="">|&lt;&lt;calendar thismonth&gt;&gt;|&lt;&lt;calendar nextmonth&gt;&gt;|</div>
<div tiddler="CalendarPlugin" modifier="AshinWima" modified="200605310500" created="200603090215" tags="systemConfig">/***\n''Name:'' Calendar plugin\n''Version:'' &lt;&lt;getversion calendar&gt;&gt; (&lt;&lt;getversiondate calendar &quot;DD MMM YYYY&quot;&gt;&gt;)\n''Author:'' SteveRumsby\n\n''Configuration:''\n\n|''First day of week:''|&lt;&lt;option txtCalFirstDay&gt;&gt;|(Monday = 0, Sunday = 6)|\n|''First day of weekend:''|&lt;&lt;option txtCalStartOfWeekend&gt;&gt;|(Monday = 0, Sunday = 6)|\n\n''Syntax:'' \n|{{{&lt;&lt;calendar&gt;&gt;}}}|Produce a full-year calendar for the current year|\n|{{{&lt;&lt;calendar year&gt;&gt;}}}|Produce a full-year calendar for the given year|\n|{{{&lt;&lt;calendar year month&gt;&gt;}}}|Produce a one-month calendar for the given month and year|\n|{{{&lt;&lt;calendar thismonth&gt;&gt;}}}|Produce a one-month calendar for the current month|\n|{{{&lt;&lt;calendar lastmonth&gt;&gt;}}}|Produce a one-month calendar for last month|\n|{{{&lt;&lt;calendar nextmonth&gt;&gt;}}}|Produce a one-month calendar for next month|\n\n***/\n// //Modify this section to change the text displayed for the month and day names, to a different language for example. You can also change the format of the tiddler names linked to from each date, and the colours used.\n\n// // ''Changes by ELS 2005.10.30:''\n// // config.macros.calendar.handler()\n// // ^^use &quot;tbody&quot; element for IE compatibility^^\n// // ^^IE returns 2005 for current year, FF returns 105... fix year adjustment accordingly^^\n// // createCalendarDays()\n// // ^^use showDate() function (if defined) to render autostyled date with linked popup^^\n// // calendar stylesheet definition\n// // ^^use .calendar class-specific selectors, add text centering and margin settings^^\n\n//{{{\nconfig.macros.calendar = {};\n\nconfig.macros.calendar.monthnames = [&quot;Jan&quot;, &quot;Feb&quot;, &quot;Mar&quot;, &quot;Apr&quot;, &quot;May&quot;, &quot;Jun&quot;, &quot;Jul&quot;, &quot;Aug&quot;, &quot;Sep&quot;, &quot;Oct&quot;, &quot;Nov&quot;, &quot;Dec&quot;];\nconfig.macros.calendar.daynames = [&quot;mon&quot;, &quot;tue&quot;, &quot;wed&quot;, &quot;thu&quot;, &quot;fri&quot;, &quot;sat&quot;, &quot;sun&quot;];\n\nconfig.macros.calendar.weekendbg = &quot;#eeeedd&quot;;\nconfig.macros.calendar.monthbg = &quot;#eeeedd&quot;;\nconfig.macros.calendar.holidaybg = &quot;#eeeedd&quot;;\n\n//}}}\n// //''Code section:''\n// (you should not need to alter anything below here)//\n//{{{\nif(config.options.txtCalFirstDay == undefined)\n config.options.txtCalFirstDay = 0;\nif(config.options.txtCalStartOfWeekend == undefined)\n config.options.txtCalStartOfWeekend = 5;\n\nconfig.macros.calendar.tiddlerformat = &quot;0DD/0MM/YYYY&quot;; // This used to be changeable - for now, it isn't// &lt;&lt;smiley :-(&gt;&gt; \n\nversion.extensions.calendar = { major: 0, minor: 6, revision: 0, date: new Date(2006, 1, 22)};\nconfig.macros.calendar.monthdays = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n\nconfig.macros.calendar.holidays = [ ]; // Not sure this is required anymore - use reminders instead\n//}}}\n\n// //Is the given date a holiday?\n//{{{\nfunction calendarIsHoliday(date)\n{\n var longHoliday = date.formatString(&quot;0DD/0MM/YYYY&quot;);\n var shortHoliday = date.formatString(&quot;0DD/0MM&quot;);\n\n for(var i = 0; i &lt; config.macros.calendar.holidays.length; i++) {\n if(config.macros.calendar.holidays[i] == longHoliday || config.macros.calendar.holidays[i] == shortHoliday) {\n return true;\n }\n }\n return false;\n}\n//}}}\n\n// //The main entry point - the macro handler.\n// //Decide what sort of calendar we are creating (month or year, and which month or year)\n// // Create the main calendar container and pass that to sub-ordinate functions to create the structure.\n// ELS 2005.10.30: added creation and use of &quot;tbody&quot; for IE compatibility and fixup for year &gt;1900//\n// ELS 2005.10.30: fix year calculation for IE's getYear() function (which returns '2005' instead of '105')//\n//{{{\nconfig.macros.calendar.handler = function(place,macroName,params)\n{\n var calendar = createTiddlyElement(place, &quot;table&quot;, null, &quot;calendar&quot;, null);\n var tbody = createTiddlyElement(calendar, &quot;tbody&quot;, null, null, null);\n var today = new Date();\n var year = today.getYear();\n if (year&lt;1900) year+=1900;\n if (params[0] == &quot;thismonth&quot;)\n {\n cacheReminders(new Date(year, today.getMonth(), 1, 0, 0), 31);\n createCalendarOneMonth(tbody, year, today.getMonth());\n } \n else if (params[0] == &quot;lastmonth&quot;) {\n var month = today.getMonth()-1; if (month==-1) { month=11; year--; }\n cacheReminders(new Date(year, month, 1, 0, 0), 31);\n createCalendarOneMonth(tbody, year, month);\n }\n else if (params[0] == &quot;nextmonth&quot;) {\n var month = today.getMonth()+1; if (month&gt;11) { month=0; year++; }\n cacheReminders(new Date(year, month, 1, 0, 0), 31);\n createCalendarOneMonth(tbody, year, month);\n }\n else {\n if (params[0]) year = params[0];\n if(params[1])\n {\n cacheReminders(new Date(year, params[1]-1, 1, 0, 0), 31);\n createCalendarOneMonth(tbody, year, params[1]-1);\n }\n else\n {\n cacheReminders(new Date(year, 0, 1, 0, 0), 366);\n createCalendarYear(tbody, year);\n }\n }\n window.reminderCacheForCalendar = null;\n}\n//}}}\n//{{{\n//This global variable is used to store reminders that have been cached\n//while the calendar is being rendered. It will be renulled after the calendar is fully rendered.\nwindow.reminderCacheForCalendar = null;\n//}}}\n//{{{\nfunction cacheReminders(date, leadtime)\n{\n if (window.findTiddlersWithReminders == null)\n return;\n window.reminderCacheForCalendar = {};\n var leadtimeHash = [];\n leadtimeHash [0] = 0;\n leadtimeHash [1] = leadtime;\n var t = findTiddlersWithReminders(date, leadtimeHash, null, 1);\n for(var i = 0; i &lt; t.length; i++) {\n //just tag it in the cache, so that when we're drawing days, we can bold this one.\n window.reminderCacheForCalendar[t[i][&quot;matchedDate&quot;]] = &quot;reminder:&quot; + t[i][&quot;params&quot;][&quot;title&quot;]; \n }\n}\n//}}}\n//{{{\nfunction createCalendarOneMonth(calendar, year, mon)\n{\n var row = createTiddlyElement(calendar, &quot;tr&quot;, null, null, null);\n createCalendarMonthHeader(calendar, row, config.macros.calendar.monthnames[mon] + &quot; &quot; + year, true, year, mon);\n row = createTiddlyElement(calendar, &quot;tr&quot;, null, null, null);\n createCalendarDayHeader(row, 1);\n createCalendarDayRowsSingle(calendar, year, mon);\n}\n//}}}\n\n//{{{\nfunction createCalendarMonth(calendar, year, mon)\n{\n var row = createTiddlyElement(calendar, &quot;tr&quot;, null, null, null);\n createCalendarMonthHeader(calendar, row, config.macros.calendar.monthnames[mon] + &quot; &quot; + year, false, year, mon);\n row = createTiddlyElement(calendar, &quot;tr&quot;, null, null, null);\n createCalendarDayHeader(row, 1);\n createCalendarDayRowsSingle(calendar, year, mon);\n}\n//}}}\n\n//{{{\nfunction createCalendarYear(calendar, year)\n{\n var row;\n row = createTiddlyElement(calendar, &quot;tr&quot;, null, null, null);\n var back = createTiddlyElement(row, &quot;td&quot;, null, null, null);\n var backHandler = function() {\n removeChildren(calendar);\n createCalendarYear(calendar, year-1);\n };\n createTiddlyButton(back, &quot;�&quot;, &quot;Previous year&quot;, backHandler);\n back.align = &quot;center&quot;;\n\n var yearHeader = createTiddlyElement(row, &quot;td&quot;, null, &quot;calendarYear&quot;, year);\n yearHeader.align = &quot;center&quot;;\n yearHeader.setAttribute(&quot;colSpan&quot;, 19);\n\n var fwd = createTiddlyElement(row, &quot;td&quot;, null, null, null);\n var fwdHandler = function() {\n removeChildren(calendar);\n createCalendarYear(calendar, year+1);\n };\n createTiddlyButton(fwd, &quot;�&quot;, &quot;Next year&quot;, fwdHandler);\n fwd.align = &quot;center&quot;;\n\n createCalendarMonthRow(calendar, year, 0);\n createCalendarMonthRow(calendar, year, 3);\n createCalendarMonthRow(calendar, year, 6);\n createCalendarMonthRow(calendar, year, 9);\n}\n//}}}\n\n//{{{\nfunction createCalendarMonthRow(cal, year, mon)\n{\n var row = createTiddlyElement(cal, &quot;tr&quot;, null, null, null);\n createCalendarMonthHeader(cal, row, config.macros.calendar.monthnames[mon], false, year, mon);\n createCalendarMonthHeader(cal, row, config.macros.calendar.monthnames[mon+1], false, year, mon);\n createCalendarMonthHeader(cal, row, config.macros.calendar.monthnames[mon+2], false, year, mon);\n row = createTiddlyElement(cal, &quot;tr&quot;, null, null, null);\n createCalendarDayHeader(row, 3);\n createCalendarDayRows(cal, year, mon);\n}\n//}}}\n\n//{{{\nfunction createCalendarMonthHeader(cal, row, name, nav, year, mon)\n{\n var month;\n if(nav) {\n var back = createTiddlyElement(row, &quot;td&quot;, null, null, null);\n back.align = &quot;center&quot;;\n back.style.background = config.macros.calendar.monthbg;\n\n/*\n back.setAttribute(&quot;colSpan&quot;, 2);\n\n var backYearHandler = function() {\n var newyear = year-1;\n removeChildren(cal);\n cacheReminders(new Date(newyear, mon , 1, 0, 0), 31);\n createCalendarOneMonth(cal, newyear, mon);\n };\n createTiddlyButton(back, &quot;&lt;&lt;&quot;, &quot;Previous year&quot;, backYearHandler);\n*/\n var backMonHandler = function() {\n var newyear = year;\n var newmon = mon-1;\n if(newmon == -1) { newmon = 11; newyear = newyear-1;}\n removeChildren(cal);\n cacheReminders(new Date(newyear, newmon , 1, 0, 0), 31);\n createCalendarOneMonth(cal, newyear, newmon);\n };\n createTiddlyButton(back, &quot;�&quot;, &quot;Previous month&quot;, backMonHandler);\n\n\n month = createTiddlyElement(row, &quot;td&quot;, null, &quot;calendarMonthname&quot;, name)\n// month.setAttribute(&quot;colSpan&quot;, 3);\n month.setAttribute(&quot;colSpan&quot;, 5);\n\n var fwd = createTiddlyElement(row, &quot;td&quot;, null, null, null);\n fwd.align = &quot;center&quot;;\n fwd.style.background = config.macros.calendar.monthbg; \n\n// fwd.setAttribute(&quot;colSpan&quot;, 2);\n var fwdMonHandler = function() {\n var newyear = year;\n var newmon = mon+1;\n if(newmon == 12) { newmon = 0; newyear = newyear+1;}\n removeChildren(cal);\n cacheReminders(new Date(newyear, newmon , 1, 0, 0), 31);\n createCalendarOneMonth(cal, newyear, newmon);\n };\n createTiddlyButton(fwd, &quot;�&quot;, &quot;Next month&quot;, fwdMonHandler);\n/*\n var fwdYear = createTiddlyElement(row, &quot;td&quot;, null, null, null);\n var fwdYearHandler = function() {\n var newyear = year+1;\n removeChildren(cal);\n cacheReminders(new Date(newyear, mon , 1, 0, 0), 31);\n createCalendarOneMonth(cal, newyear, mon);\n };\n createTiddlyButton(fwd, &quot;&gt;&gt;&quot;, &quot;Next year&quot;, fwdYearHandler);\n*/\n } else {\n month = createTiddlyElement(row, &quot;td&quot;, null, &quot;calendarMonthname&quot;, name)\n month.setAttribute(&quot;colSpan&quot;, 7);\n }\n month.align = &quot;center&quot;;\n month.style.background = config.macros.calendar.monthbg;\n}\n//}}}\n\n//{{{\nfunction createCalendarDayHeader(row, num)\n{\n var cell;\n for(var i = 0; i &lt; num; i++) {\n for(var j = 0; j &lt; 7; j++) {\n var d = j + (config.options.txtCalFirstDay - 0);\n if(d &gt; 6) d = d - 7;\n cell = createTiddlyElement(row, &quot;td&quot;, null, null, config.macros.calendar.daynames[d]);\n\n // if(d == (config.options.txtCalStartOfWeekend-0) || d == (config.options.txtCalStartOfWeekend-0+1))\n // cell.style.background = config.macros.calendar.weekendbg;\n }\n }\n}\n//}}}\n\n//{{{\nfunction createCalendarDays(row, col, first, max, year, mon)\n{\n var i;\n for(i = 0; i &lt; col; i++) {\n createTiddlyElement(row, &quot;td&quot;, null, null, null);\n }\n var day = first;\n for(i = col; i &lt; 7; i++) {\n var d = i + (config.options.txtCalFirstDay - 0);\n if(d &gt; 6) d = d - 7;\n var daycell = createTiddlyElement(row, &quot;td&quot;, null, null, null);\n var isaWeekend = ((d == (config.options.txtCalStartOfWeekend-0) || d == (config.options.txtCalStartOfWeekend-0+1))? true:false);\n\n if(day &gt; 0 &amp;&amp; day &lt;= max) {\n var celldate = new Date(year, mon, day);\n // ELS 2005.10.30: use &lt;&lt;date&gt;&gt; macro's showDate() function to create popup\n if (window.showDate) {\n showDate(daycell,celldate,&quot;popup&quot;,&quot;DD&quot;,&quot;DD-MMM-YYYY&quot;,true, isaWeekend); \n } else {\n // if(isaWeekend) daycell.style.background = config.macros.calendar.weekendbg;\n var title = celldate.formatString(config.macros.calendar.tiddlerformat);\n if(calendarIsHoliday(celldate)) {\n daycell.style.background = config.macros.calendar.holidaybg;\n }\n if(window.findTiddlersWithReminders == null) {\n var link = createTiddlyLink(daycell, title, false);\n link.appendChild(document.createTextNode(day));\n } else {\n var button = createTiddlyButton(daycell, day, title, onClickCalendarDate);\n }\n }\n }\n day++;\n }\n}\n//}}}\n\n// //We've clicked on a day in a calendar - create a suitable pop-up of options.\n// //The pop-up should contain:\n// // * a link to create a new entry for that date\n// // * a link to create a new reminder for that date\n// // * an &lt;hr&gt;\n// // * the list of reminders for that date\n//{{{\nfunction onClickCalendarDate(e)\n{\n var button = this;\n var date = button.getAttribute(&quot;title&quot;);\n var dat = new Date(date.substr(6,4), date.substr(3,2)-1, date.substr(0, 2));\n\n date = dat.formatString(config.macros.calendar.tiddlerformat);\n var popup = createTiddlerPopup(this);\n popup.appendChild(document.createTextNode(date));\n var newReminder = function() {\n var t = store.getTiddlers(date);\n displayTiddler(null, date, 2, null, null, false, false);\n if(t) {\n document.getElementById(&quot;editorBody&quot; + date).value += &quot;\sn&lt;&lt;reminder day:&quot; + dat.getDate() +\n &quot; month:&quot; + (dat.getMonth()+1) +\n &quot; year:&quot; + (dat.getYear()+1900) + &quot; title: &gt;&gt;&quot;;\n } else {\n document.getElementById(&quot;editorBody&quot; + date).value = &quot;&lt;&lt;reminder day:&quot; + dat.getDate() +\n &quot; month:&quot; + (dat.getMonth()+1) +\n &quot; year:&quot; + (dat.getYear()+1900) + &quot; title: &gt;&gt;&quot;;\n }\n };\n var link = createTiddlyButton(popup, &quot;New reminder&quot;, null, newReminder); \n popup.appendChild(document.createElement(&quot;hr&quot;));\n\n var t = findTiddlersWithReminders(dat, [0,14], null, 1);\n for(var i = 0; i &lt; t.length; i++) {\n link = createTiddlyLink(popup, t[i].tiddler, false);\n link.appendChild(document.createTextNode(t[i].tiddler));\n }\n}\n//}}}\n\n//{{{\nfunction calendarMaxDays(year, mon)\n{\n var max = config.macros.calendar.monthdays[mon];\n if(mon == 1 &amp;&amp; (year % 4) == 0 &amp;&amp; ((year % 100) != 0 || (year % 400) == 0)) {\n max++;\n }\n return max;\n}\n//}}}\n\n//{{{\nfunction createCalendarDayRows(cal, year, mon)\n{\n var row = createTiddlyElement(cal, &quot;tr&quot;, null, null, null);\n\n var first1 = (new Date(year, mon, 1)).getDay() -1 - (config.options.txtCalFirstDay-0);\n if(first1 &lt; 0) first1 = first1 + 7;\n var day1 = -first1 + 1;\n var first2 = (new Date(year, mon+1, 1)).getDay() -1 - (config.options.txtCalFirstDay-0);\n if(first2 &lt; 0) first2 = first2 + 7;\n var day2 = -first2 + 1;\n var first3 = (new Date(year, mon+2, 1)).getDay() -1 - (config.options.txtCalFirstDay-0);\n if(first3 &lt; 0) first3 = first3 + 7;\n var day3 = -first3 + 1;\n\n var max1 = calendarMaxDays(year, mon);\n var max2 = calendarMaxDays(year, mon+1);\n var max3 = calendarMaxDays(year, mon+2);\n\n while(day1 &lt;= max1 || day2 &lt;= max2 || day3 &lt;= max3) {\n row = createTiddlyElement(cal, &quot;tr&quot;, null, null, null);\n createCalendarDays(row, 0, day1, max1, year, mon); day1 += 7;\n createCalendarDays(row, 0, day2, max2, year, mon+1); day2 += 7;\n createCalendarDays(row, 0, day3, max3, year, mon+2); day3 += 7;\n }\n}\n//}}}\n\n//{{{\nfunction createCalendarDayRowsSingle(cal, year, mon)\n{\n var row = createTiddlyElement(cal, &quot;tr&quot;, null, null, null);\n\n var first1 = (new Date(year, mon, 1)).getDay() -1 - (config.options.txtCalFirstDay-0);\n if(first1 &lt; 0) first1 = first1+ 7;\n var day1 = -first1 + 1;\n var max1 = calendarMaxDays(year, mon);\n\n while(day1 &lt;= max1) {\n row = createTiddlyElement(cal, &quot;tr&quot;, null, null, null);\n createCalendarDays(row, 0, day1, max1, year, mon); day1 += 7;\n }\n}\n//}}}\n\n</div>
<div tiddler="ClustomFormatter" modifier="SimonBaird" modified="200603080147" created="200603080147" tags="systemConfig">//{{{\nconfig.formatters.push( {\n name: &quot;customClasses&quot;,\n match: &quot;{{&quot;,\n lookahead: &quot;{{[\s\ss]*([\s\sw]+[\s\ss\s\sw]*)[\s\ss]*{((?:[^}]|(?:}(?!}))|(?:}}(?!})))*)}}}&quot;,\n handler: function(w){\n var lookaheadRegExp = new RegExp(this.lookahead,&quot;mg&quot;);\n lookaheadRegExp.lastIndex = w.matchStart;\n var lookaheadMatch = lookaheadRegExp.exec(w.source);\n var p = createTiddlyElement(w.output,&quot;span&quot;,null,lookaheadMatch[1]);\n wikify( lookaheadMatch[2], p, null, w.tiddler);\n w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;\n }\n});\n\nconfig.formatters.push( {\n name: &quot;customClasses2&quot;,\n match: &quot;{div{&quot;,\n lookahead: &quot;{div{[\s\ss]*([\s\sw]+[\s\ss\s\sw]*)[\s\ss]*{((?:[^}]|(?:}(?!}))|(?:}}(?!})))*)}}}&quot;,\n handler: function(w){\n var lookaheadRegExp = new RegExp(this.lookahead,&quot;mg&quot;);\n lookaheadRegExp.lastIndex = w.matchStart;\n var lookaheadMatch = lookaheadRegExp.exec(w.source);\n var p = createTiddlyElement(w.output,&quot;div&quot;,null,lookaheadMatch[1]);\n wikify( lookaheadMatch[2], p, null, w.tiddler);\n w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;\n }\n});\n\n\n//}}}</div>
<div tiddler="Context" modifier="ashinw" modified="200608040305" created="200603080228" tags="">You can change what your contexts are by renaming, adding to or removing these tiddlers.</div>
<div tiddler="ContextViewTemplate" modifier="YourName" modified="200605070808" created="200604230222" tags="ViewTemplates">&lt;!---\n| Name:|ContextViewTemplate |\n| Version:||\n| Source:|http://simonbaird.com/mptw/|\n---&gt;\n&lt;!--{{{--&gt;\n&lt;div class=&quot;toolbar&quot; macro=&quot;toolbar -closeTiddler closeOthers +editTiddler permalink references jump newHere&quot;&gt;&lt;/div&gt;\n&lt;div class=&quot;tagglyTagged&quot; macro=&quot;hideSomeTags&quot;&gt;&lt;/div&gt;\n&lt;div&gt;&lt;span class=&quot;title&quot; macro=&quot;view title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;miniTag&quot; macro=&quot;miniTag&quot;&gt;&lt;/span&gt;&lt;/div&gt;\n&lt;div class='subtitle'&gt;Created &lt;span macro='view created date [[DD/MM/YY]]'&gt;&lt;/span&gt;, updated &lt;span macro='view modified date [[DD/MM/YY]]'&gt;&lt;/span&gt;&lt;/div&gt;\n&lt;div class=&quot;viewer&quot; macro=&quot;view text wikified&quot;&gt;&lt;/div&gt;\n\n&lt;table width=&quot;100%&quot;&gt;&lt;tr&gt;\n&lt;td valign=&quot;top&quot; style=&quot;font-size:90%;border-right:1px dashed #888;padding:0.5em;&quot;&gt;\n&lt;xmp macro=&quot;wikifyContents&quot; class=&quot;viewer&quot;&gt;\n{div{nextAction{[[Next Actions|Next]] \s\n&lt;&lt;forEachTiddler where 'tiddler.title == &quot;SiteTitle&quot;'\n write '&quot;&lt;&lt;newerTiddler button:\s&quot;new\s&quot; tags:\s&quot;Next Task [[&quot; + \n context.inTiddler.title + \n &quot;]]\s&quot; name:\s&quot;New Task\s&quot; $))&quot;'&gt;&gt; \s\n&lt;&lt;forEachTiddler\n where 'tiddler.tags.containsAll([&quot;Task&quot;,&quot;Next&quot;,context.inTiddler.title]) &amp;&amp; !tiddler.tags.contains(&quot;Done&quot;)'&gt;&gt;}}}\n{div{nextAction{[[Waiting For|Wait]] \s\n&lt;&lt;forEachTiddler where 'tiddler.title == &quot;SiteTitle&quot;'\n write '&quot;&lt;&lt;newerTiddler button:\s&quot;new\s&quot; tags:\s&quot;Wait Task [[&quot; + \n context.inTiddler.title + \n &quot;]]\s&quot; name:\s&quot;New Task\s&quot; $))&quot;'&gt;&gt; \s\n&lt;&lt;forEachTiddler\n where 'tiddler.tags.containsAll([&quot;Task&quot;,&quot;Wait&quot;,context.inTiddler.title]) &amp;&amp; !tiddler.tags.contains(&quot;Done&quot;)'&gt;&gt;}}}\n&lt;&lt;forEachTiddler where 'tiddler.tags.containsAll([context.inTiddler.title, &quot;Task&quot;]) &amp;&amp; !tiddler.tags.contains(&quot;Done&quot;) &amp;&amp; !tiddler.tags.contains(&quot;Next&quot;) &amp;&amp; !tiddler.tags.contains(&quot;Wait&quot;) &amp;&amp; !tiddler.tags.contains(&quot;Someday&quot;)'\n write\n '&quot;@@font-size:90%;padding-left:0.5em;[[&quot; + tiddler.title + &quot;]]@@ \sn&quot;'\n&gt;&gt;\n----\n[[Someday/Maybe|Someday]] \s\n&lt;&lt;forEachTiddler where 'tiddler.title == &quot;SiteTitle&quot;'\n write '&quot;&lt;&lt;newerTiddler button:\s&quot;new\s&quot; tags:\s&quot;Someday Task [[&quot; + \n context.inTiddler.title + \n &quot;]]\s&quot; name:\s&quot;New Task\s&quot; $))&quot;'&gt;&gt; \s\n\s\n&lt;&lt;forEachTiddler\n where 'tiddler.tags.containsAll([&quot;Task&quot;,&quot;Someday&quot;,context.inTiddler.title]) &amp;&amp; !tiddler.tags.contains(&quot;Done&quot;)'&gt;&gt;\n----\n[[Done]] \s\n{div{scrolling{\s\n&lt;&lt;forEachTiddler\n where 'tiddler.tags.containsAll([&quot;Task&quot;,context.inTiddler.title]) &amp;&amp; tiddler.tags.contains(&quot;Done&quot;)'\n sortBy 'tiddler.modified' descending &gt;&gt;\s\n}}}\n\n&lt;/xmp&gt;\n&lt;/td&gt;\n\n\n&lt;td valign=&quot;top&quot; style=&quot;font-size:90%;padding:0.5em;&quot;&gt;\n&lt;xmp macro=&quot;wikifyContents&quot; class=&quot;viewer&quot;&gt;\n/% ha ha!! better way???? it's like select 'thing' thing from dual %/ \s\n[[Reminders|Reminder]] &lt;&lt;forEachTiddler where 'tiddler.title == &quot;SiteTitle&quot;'\n write '&quot;&lt;&lt;newerTiddler button:\s&quot;new\s&quot; tags:\s&quot;Reminder [[&quot; + \n context.inTiddler.title + \n &quot;]]\s&quot; name:\s&quot;New Reminder\s&quot; text:\s&quot;&lt;&lt;newReminder$}}\s&quot;$))&quot;'&gt;&gt;++++\n&lt;&lt;forEachTiddler where 'tiddler.title == &quot;SiteTitle&quot;' write\n '&quot;&lt;&lt;showReminders tag:\s&quot;&quot; + context.inTiddler.title + \n &quot;\s&quot; format:\s&quot;*DIFF, TITLE\s&quot;$))&quot; ' &gt;&gt;===\n----\n[[Tasks|Task]] by [[Project]]\n&lt;&lt;forEachTiddler\n where 'tiddler.tags.contains(&quot;Project&quot;)'\n sortBy 'tiddler.title'\n write\n '&quot;@@font-size:90%;padding-left:0.5em;[[&quot; + tiddler.title + &quot;]]@@ &quot;+\n/// display a count (by Clint)\n&quot;&lt;&lt;forEachTiddler where \sn&quot; +\n &quot; \s'tiddler.tags.containsAll([\s&quot;Task\s&quot;,\s&quot;&quot;+tiddler.title+&quot;\s&quot;,\s&quot;&quot;+context.inTiddler.title+&quot;\s&quot;]) &amp;&amp; &quot;+\n &quot; !tiddler.tags.contains(\s&quot;Done\s&quot;)\s'\sn&quot; +\n &quot; script \s'function writeTotalTasks(index, count) {if (index == 0) return \s&quot;(\s&quot;+count+\s&quot;)\s&quot;; else return \s&quot;\s&quot;;}\s' &quot;+\n &quot;write \s'writeTotalTasks(index,count)\s'$))&quot; +\n/// end display a count \n&quot;&lt;&lt;newerTiddler name:\s&quot;New Task\s&quot; button:\s&quot;new\s&quot; text:\s&quot;Enter task details\s&quot; tags:\s&quot;Task [[&quot;+tiddler.title+&quot;]] [[&quot;+context.inTiddler.title+&quot;]]\s&quot;$))&quot; +\n (tiddler.tags.contains(&quot;startCollapsed&quot;)?&quot;+++\sn&quot;:&quot;++++&quot;) +\n &quot;&lt;&lt;forEachTiddler where \sn&quot; +\n&quot; \s'tiddler.tags.containsAll([\s&quot;&quot;+context.inTiddler.title+&quot;\s&quot;,\s&quot;Task\s&quot;,\s&quot;&quot;+tiddler.title+&quot;\s&quot;]) &amp;&amp; &quot;+\n &quot; !tiddler.tags.contains(\s&quot;Done\s&quot;)\s'\sn&quot; +\n &quot;$))&quot; +\n &quot;===\sn\sn&quot; +\n &quot;&quot;'\n&gt;&gt;\n\n&lt;/xmp&gt;\n&lt;/td&gt;\n&lt;/tr&gt;&lt;/table&gt;\n&lt;br class=&quot;tagClear&quot;/&gt;\n&lt;!-- &lt;div class=&quot;tagglyTagging&quot; macro=&quot;tagglyListWithSort&quot;&gt;&lt;/div&gt; --&gt;\n\n&lt;!--}}}--&gt;\n\n\n</div>
<div tiddler="Create a new branch for the GWT community to commit their updates" modifier="ashinw" modified="200608051849" created="200608051844" tags="Next Task [[Gems-GWT-User project roadmap]]">Currently the only branch that exists is gworks-1.0.21. However, many members of the GWT community have already identified fixed for various defects or have required various aspects of the framework opened up. These developers should be able to commit their changes.\n* create a new branch at following location and copy it from tags/1.0.21:\n** https://gems-gwt-user.googlecode.com/svn/branches/gwt-community-1.0.21</div>
<div tiddler="Dashboard" modifier="SimonBaird" modified="200604261339" created="200603060510" tags="TaskDashboard"></div>
<div tiddler="DatePlugin" modifier="AshinWima" modified="200605310521" created="200603090215" tags="systemConfig">/***\n''Date Plugin for TiddlyWiki version 2.x''\n^^author: Eric Shulman - ELS Design Studios\nsource: http://www.TiddlyTools.com/#DatePlugin\nlicense: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^\n^^last update: &lt;&lt;date tiddler &quot;DDD, MMM DDth, YYYY hh:0mm:0ss&quot;&gt;&gt;^^\n\nThere are quite a few calendar generators, reminders, to-do lists, 'dated tiddlers' journals, blog-makers and GTD-like schedule managers that have been built around TW. While they all have different purposes, and vary in format, interaction, and style, in one way or another each of these plugins displays and/or uses date-based information to make finding, accessing and managing relevant tiddlers easier. This plugin provides a general approach to embedding dates and date-based links/menus within tiddler content.\n\nYou can ''specify a date using a combination of year, month, and day number values or mathematical expressions (such as &quot;Y+1&quot; or &quot;D+30&quot;)'', and then just display it as formatted date text, or create a ''link to a 'dated tiddler''' for quick blogging, or create a ''popup menu'' containing the dated tiddler link plus links to ''tiddlers that were changed'' as well as any ''scheduled reminders'' for that date.\n!!!!!Usage\n&lt;&lt;&lt;\nWhen installed, this plugin defines a macro: {{{&lt;&lt;date [mode] [date] [format] [linkformat]&gt;&gt;}}}. All of the macro parameters are optional and, in it's simplest form, {{{&lt;&lt;date&gt;&gt;}}}, it is equivalent to the ~TiddlyWiki core macro, {{{&lt;&lt;today&gt;&gt;}}}.\n\nHowever, where {{{&lt;&lt;today&gt;&gt;}}} simply inserts the current date/time in a predefined format (or custom format, using {{{&lt;&lt;today [format]&gt;&gt;}}}), the {{{&lt;&lt;date&gt;&gt;}}} macro's parameters take it much further than that:\n* [mode] is either ''display'', ''link'' or ''popup''. If omitted, it defaults to ''display''. This param let's you select between simply displaying a formatted date, or creating a link to a specific 'date titled' tiddler or a popup menu containing a dated tiddler link, plus links to changes and reminders.\n* [date] lets you enter ANY date (not just today) as ''year, month, and day values or simple mathematical expressions'' using pre-defined variables, Y, M, and D for the current year, month and day, repectively. You can display the modification date of the current tiddler by using the keyword: ''tiddler'' in place of the year, month and day parameters. Use ''tiddler://name-of-tiddler//'' to display the modification date of a specific tiddler. You can also use keywords ''today'' or ''filedate'' to refer to these //dynamically changing// date/time values. \n* [format] and [linkformat] uses standard ~TiddlyWiki date formatting syntax. The default is &quot;YYYY.0MM.0DD&quot;\n&gt;^^''DDD'' - day of week in full (eg, &quot;Monday&quot;), ''DD'' - day of month, ''0DD'' - adds leading zero^^\n&gt;^^''MMM'' - month in full (eg, &quot;July&quot;), ''MM'' - month number, ''0MM'' - adds leading zero^^\n&gt;^^''YYYY'' - full year, ''YY'' - two digit year, ''hh'' - hours, ''mm'' - minutes, ''ss'' - seconds^^\n&gt;^^//note: use of hh, mm or ss format codes is only supported with ''tiddler'', ''today'' or ''filedate'' values//^^\n* [linkformat] - specify an alternative date format so that the title of a 'dated tiddler' link can have a format that differs from the date's displayed format\n\nIn addition to the macro syntax, DatePlugin also provides a public javascript API so that other plugins that work with dates (such as calendar generators, etc.) can quickly incorporate date formatted links or popups into their output:\n\n''{{{showDate(place, date, mode, format, linkformat, autostyle, weekend)}}}'' \n\nNote that in addition to the parameters provided by the macro interface, the javascript API also supports two optional true/false parameters:\n* [autostyle] - when true, the font/background styles of formatted dates are automatically adjusted to show the date's status: 'today' is boxed, 'changes' are bold, 'reminders' are underlined, while weekends and holidays (as well as changes and reminders) can each have a different background color to make them more visibly distinct from each other.\n* [weekend] - true indicates a weekend, false indicates a weekday. When this parameter is omitted, the plugin uses internal defaults to automatically determine when a given date falls on a weekend.\n&lt;&lt;&lt;\n!!!!!Examples\n&lt;&lt;&lt;\nThe current date: &lt;&lt;date&gt;&gt;\nThe current time: &lt;&lt;date today &quot;0hh:0mm:0ss&quot;&gt;&gt;\nToday's blog: &lt;&lt;date link today &quot;DDD, MMM DDth, YYYY&quot;&gt;&gt;\nRecent blogs/changes/reminders: &lt;&lt;date popup Y M D-1 &quot;yesterday&quot;&gt;&gt; &lt;&lt;date popup today &quot;today&quot;&gt;&gt; &lt;&lt;date popup Y M D+1 &quot;tomorrow&quot;&gt;&gt;\nThe first day of next month will be a &lt;&lt;date Y M+1 1 &quot;DDD&quot;&gt;&gt;\nThis tiddler (DatePlugin) was last updated on: &lt;&lt;date tiddler &quot;DDD, MMM DDth, YYYY&quot;&gt;&gt;\nThe SiteUrl was last updated on: &lt;&lt;date tiddler:SiteUrl &quot;DDD, MMM DDth, YYYY&quot;&gt;&gt;\nThis document was last saved on &lt;&lt;date filedate &quot;DDD, MMM DDth, YYYY at 0hh:0mm:0ss&quot;&gt;&gt;\n&lt;&lt;date 2006 07 24 &quot;MMM DDth, YYYY&quot;&gt;&gt; will be a &lt;&lt;date 2006 07 24 &quot;DDD&quot;&gt;&gt;\n&lt;&lt;&lt;\n!!!!!Installation\n&lt;&lt;&lt;\nimport (or copy/paste) the following tiddlers into your document:\n''DatePlugin'' (tagged with &lt;&lt;tag systemConfig&gt;&gt;)\n&lt;&lt;&lt;\n!!!!!Revision History\n&lt;&lt;&lt;\n''2006.02.14 [2.0.5]''\nwhen readOnly is set (by TW core), omit &quot;new reminders...&quot; popup menu item and, if a &quot;dated tiddler&quot; does not already exist, display the date as simple text instead of a link.\n''2006.02.05 [2.0.4]''\nadded var to variables that were unintentionally global. Avoids FireFox 1.5.0.1 crash bug when referencing global variables\n''2006.01.18 [2.0.3]''\nIn 1.2.x the tiddler editor's text area control was given an element ID=(&quot;tiddlerBody&quot;+title), so that it was easy to locate this field and programmatically modify its content. With the addition of configuration templates in 2.x, the textarea no longer has an ID assigned. To find this control we now look through all the child nodes of the tiddler editor to locate a &quot;textarea&quot; control where attribute(&quot;edit&quot;) equals &quot;text&quot;, and then append the new reminder to the contents of that control.\n''2006.01.11 [2.0.2]''\ncorrect 'weekend' override detection logic in showDate()\n''2006.01.10 [2.0.1]''\nallow custom-defined weekend days (default defined in config.macros.date.weekend[] array)\nadded flag param to showDate() API to override internal weekend[] array\n''2005.12.27 [2.0.0]''\nUpdate for TW2.0\nAdded parameter handling for 'linkformat'\n''2005.12.21 [1.2.2]''\nFF's date.getYear() function returns 105 (for the current year, 2005). When calculating a date value from Y M and D expressions, the plugin adds 1900 to the returned year value get the current year number. But IE's date.getYear() already returns 2005. As a result, plugin calculated date values on IE were incorrect (e.g., 3905 instead of 2005). Adding +1900 is now conditional so the values will be correct on both browsers.\n''2005.11.07 [1.2.1]''\nadded support for &quot;tiddler&quot; dynamic date parameter\n''2005.11.06 [1.2.0]''\nadded support for &quot;tiddler:title&quot; dynamic date parameter\n''2005.11.03 [1.1.2]''\nwhen a reminder doesn't have a specified title parameter, use the title of the tiddler that contains the reminder as &quot;fallback&quot; text in the popup menu. Based on a suggestion from BenjaminKudria.\n''2005.11.03 [1.1.1]''\nTemporarily bypass hasReminders() logic to avoid excessive overhead from generating the indexReminders() cache. While reminders can still appear in the popup menu, they just won't be indicated by auto-styling the date number that is displayed. This single change saves approx. 60% overhead (5 second delay reduced to under 2 seconds).\n''2005.11.01 [1.1.0]''\ncorrected logic in hasModifieds() and hasReminders() so caching of indexed modifieds and reminders is done just once, as intended. This should hopefully speed up calendar generators and other plugins that render multiple dates...\n''2005.10.31 [1.0.1]''\ndocumentation and code cleanup\n''2005.10.31 [1.0.0]''\ninitial public release\n''2005.10.30 [0.9.0]''\npre-release\n&lt;&lt;&lt;\n!!!!!Credits\n&lt;&lt;&lt;\nThis feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]].\n&lt;&lt;&lt;\n!!!!!Code\n***/\n//{{{\nversion.extensions.date = {major: 2, minor: 0, revision: 5, date: new Date(2006,2,14)};\n//}}}\n\n//{{{\n// 1.2.x compatibility\nif (!window.story) window.story=window;\nif (!store.getTiddler) store.getTiddler=function(title){return store.tiddlers[title]}\nif (!store.addTiddler) store.addTiddler=function(tiddler){store.tiddlers[tiddler.title]=tiddler}\nif (!store.deleteTiddler) store.deleteTiddler=function(title){delete store.tiddlers[title]}\n//}}}\n\n//{{{\nconfig.macros.date = {\n format: &quot;YYYY.0MM.0DD&quot;, // default date display format\n linkformat: &quot;YYYY.0MM.0DD&quot;, // 'dated tiddler' link format\n weekendbg: &quot;#eeeedd&quot;, // &quot;cocoa&quot;\n holidaybg: &quot;#eeeedd&quot;, // &quot;coffee&quot;\n modifiedsbg: &quot;#ccddcc&quot;, // &quot;beef&quot;\n remindersbg: &quot;white&quot;, // &quot;face&quot;\n holidays: [ &quot;01/01&quot;, &quot;07/04&quot;, &quot;07/24&quot;, &quot;11/24&quot; ], // NewYearsDay, IndependenceDay(US), Eric's Birthday (hooray!), Thanksgiving(US)\n weekend: [ 1,0,0,0,0,0,1 ] // [ day index values: sun=0, mon=1, tue=2, wed=3, thu=4, fri=5, sat=6 ]\n};\n//}}}\n\n//{{{\nconfig.macros.date.handler = function(place,macroName,params)\n{\n // do we want to see a link, a popup, or just a formatted date?\n var mode=&quot;display&quot;;\n if (params[0]==&quot;display&quot;) { mode=params[0]; params.shift(); }\n if (params[0]==&quot;popup&quot;) { mode=params[0]; params.shift(); }\n if (params[0]==&quot;link&quot;) { mode=params[0]; params.shift(); }\n // get the date\n var now = new Date();\n var date = now;\n if (!params[0] || params[0]==&quot;today&quot;)\n { params.shift(); }\n else if (params[0]==&quot;filedate&quot;)\n { date=new Date(document.lastModified); params.shift(); }\n else if (params[0]==&quot;tiddler&quot;)\n { date=store.getTiddler(story.findContainingTiddler(place).id.substr(7)).modified; params.shift(); }\n else if (params[0].substr(0,8)==&quot;tiddler:&quot;)\n { var t; if ((t=store.getTiddler(params[0].substr(8)))) date=t.modified; params.shift(); }\n else {\n var y = eval(params.shift().replace(/Y/ig,(now.getYear()&lt;1900)?now.getYear()+1900:now.getYear()));\n var m = eval(params.shift().replace(/M/ig,now.getMonth()+1));\n var d = eval(params.shift().replace(/D/ig,now.getDate()+0));\n date = new Date(y,m-1,d);\n }\n // date format with optional custom override\n var format=this.format; if (params[0]) format=params.shift();\n var linkformat=this.linkformat; if (params[0]) linkformat=params.shift();\n showDate(place,date,mode,format,linkformat);\n}\n//}}}\n\n//{{{\nwindow.showDate=showDate;\nfunction showDate(place,date,mode,format,linkformat,autostyle,weekend)\n{\n if (!mode) mode=&quot;display&quot;;\n if (!format) format=config.macros.date.format;\n if (!linkformat) linkformat=config.macros.date.linkformat;\n if (!autostyle) autostyle=false;\n\n // format the date output\n var title = date.formatString(format);\n var linkto = date.formatString(linkformat);\n\n // just show the formatted output\n if (mode==&quot;display&quot;) { place.appendChild(document.createTextNode(title)); return; }\n\n // link to a 'dated tiddler'\n var link = createTiddlyLink(place, linkto, false);\n link.appendChild(document.createTextNode(title));\n link.title = linkto;\n link.date = date;\n link.format = format;\n link.linkformat = linkformat;\n\n // if using a popup menu, replace click handler for dated tiddler link\n // with handler for popup and make link text non-italic (i.e., an 'existing link' look)\n if (mode==&quot;popup&quot;) {\n link.onclick = onClickDatePopup;\n link.style.fontStyle=&quot;normal&quot;;\n }\n\n // format the popup link to show what kind of info it contains (for use with calendar generators)\n if (!autostyle) return;\n if (hasModifieds(date))\n { link.style.fontStyle=&quot;normal&quot;; link.style.fontWeight=&quot;normal&quot;; }\n if (hasReminders(date))\n { link.style.textDecoration=&quot;underline&quot;; }\n if(isToday(date))\n { link.style.border=&quot;1px solid black&quot;; }\n\n if( (weekend!=undefined?weekend:isWeekend(date)) &amp;&amp; (config.macros.date.weekendbg!=&quot;&quot;) )\n { place.style.background = config.macros.date.weekendbg; }\n if(isHoliday(date)&amp;&amp;(config.macros.date.holidaybg!=&quot;&quot;))\n { place.style.background = config.macros.date.holidaybg; }\n if (hasModifieds(date)&amp;&amp;(config.macros.date.modifiedsbg!=&quot;&quot;))\n { place.style.background = config.macros.date.modifiedsbg; }\n if (hasReminders(date)&amp;&amp;(config.macros.date.remindersbg!=&quot;&quot;))\n { place.style.background = config.macros.date.remindersbg; }\n}\n//}}}\n\n//{{{\nfunction isToday(date) // returns true if date is today\n { var now=new Date(); return ((now-date&gt;=0) &amp;&amp; (now-date&lt;86400000)); }\n\nfunction isWeekend(date) // returns true if date is a weekend\n { return (config.macros.date.weekend[date.getDay()]); }\n\nfunction isHoliday(date) // returns true if date is a holiday\n{\n var longHoliday = date.formatString(&quot;0MM/0DD/YYYY&quot;);\n var shortHoliday = date.formatString(&quot;0MM/0DD&quot;);\n for(var i = 0; i &lt; config.macros.date.holidays.length; i++) {\n var holiday=config.macros.date.holidays[i];\n if (holiday==longHoliday||holiday==shortHoliday) return true;\n }\n return false;\n}\n//}}}\n\n//{{{\n// Event handler for clicking on a day popup\nfunction onClickDatePopup(e)\n{\n if (!e) var e = window.event;\n var theTarget = resolveTarget(e);\n var popup = createTiddlerPopup(this);\n if(popup) {\n // always show dated tiddler link (or just date, if readOnly) at the top...\n if (!readOnly || store.tiddlerExists(this.date.formatString(this.linkformat)))\n createTiddlyLink(popup,this.date.formatString(this.linkformat),true);\n else\n createTiddlyText(popup,this.date.formatString(this.linkformat));\n addModifiedsToPopup(popup,this.date,this.format);\n addRemindersToPopup(popup,this.date,this.linkformat);\n }\n scrollToTiddlerPopup(popup,false);\n e.cancelBubble = true;\n if (e.stopPropagation) e.stopPropagation();\n return(false);\n}\n//}}}\n\n//{{{\nfunction indexModifieds() // build list of tiddlers, hash indexed by modification date\n{\n var modifieds= { };\n var tiddlers = store.getTiddlers(&quot;title&quot;);\n for (var t = 0; t &lt; tiddlers.length; t++) {\n var date = tiddlers[t].modified.formatString(&quot;YYYY0MM0DD&quot;)\n if (!modifieds[date])\n modifieds[date]=new Array();\n modifieds[date].push(tiddlers[t].title);\n }\n return modifieds;\n}\nfunction hasModifieds(date) // returns true if date has modified tiddlers\n{\n if (!config.macros.date.modifieds) config.macros.date.modifieds = indexModifieds();\n return (config.macros.date.modifieds[date.formatString(&quot;YYYY0MM0DD&quot;)]!=undefined);\n}\n\nfunction addModifiedsToPopup(popup,when,format)\n{\n if (!config.macros.date.modifieds) config.macros.date.modifieds = indexModifieds();\n var indent=String.fromCharCode(160)+String.fromCharCode(160);\n var mods = config.macros.date.modifieds[when.formatString(&quot;YYYY0MM0DD&quot;)];\n if (mods) {\n mods.sort();\n var e=createTiddlyElement(popup,&quot;div&quot;,null,null,&quot;changes:&quot;);\n for(var t=0; t&lt;mods.length; t++) {\n var link=createTiddlyLink(popup,mods[t],false);\n link.appendChild(document.createTextNode(indent+mods[t]));\n createTiddlyElement(popup,&quot;br&quot;,null,null,null);\n }\n }\n}\n//}}}\n\n//{{{\nfunction indexReminders() // build list of tiddlers with reminders, hash indexed by reminder date\n{\n var reminders = { };\n\n if(window.findTiddlersWithReminders==undefined) return; // reminder plugin not installed\n\n var matches = store.search(&quot;reminder&quot;,false,false,&quot;title&quot;,&quot;excludeSearch&quot;);\n var macroPattern = &quot;&lt;&lt;([^&gt;\s\ss]+)(?:\s\ss*)([^&gt;]*)&gt;&gt;&quot;;\n var macroRegExp = new RegExp(macroPattern,&quot;mg&quot;);\n var arr = [];\n for(var t=matches.length-1; t&gt;=0; t--)\n {\n var targetText = matches[t].text;\n do {\n // Get the next formatting match\n var formatMatch = macroRegExp.exec(targetText);\n if(formatMatch)\n {\n if (formatMatch[1] != null &amp;&amp; formatMatch[1].toLowerCase() == &quot;reminder&quot;)\n {\n //Find the matching date.\n var params = formatMatch[2].readMacroParams();\n var dateHash = getParamsForReminder(params);\n var date = findDateForReminder(dateHash);\n if (date != null)\n {\n var dateindex = date.formatString(&quot;YYYY0MM0DD&quot;)\n if (!reminders[dateindex])\n reminders[dateindex]=new Array();\n reminders[dateindex].pushUnique(t);\n }\n }\n }\n } while(formatMatch);\n }\n return reminders;\n}\n\nfunction hasReminders(date) // returns true if date has reminders\n{\n if (window.reminderCacheForCalendar != null)\n return window.reminderCacheForCalendar[date] != null;\n return false; // ELS 2005.11.03: BYPASS due to performance issues\n if (!config.macros.date.reminders) config.macros.date.reminders = indexReminders();\n return (config.macros.date.reminders[date.formatString(&quot;YYYY0MM0DD&quot;)]!=undefined);\n}\n\nfunction addRemindersToPopup(popup,when,format)\n{\n if(window.findTiddlersWithReminders==undefined) return; // reminder plugin not installed\n\n var indent = String.fromCharCode(160)+String.fromCharCode(160);\n var reminders=findTiddlersWithReminders(when, [0,31],null,1);\n var e=createTiddlyElement(popup,&quot;div&quot;,null,null,&quot;reminders:&quot;+(!reminders.length?&quot; none&quot;:&quot;&quot;));\n for(var t=0; t&lt;reminders.length; t++) {\n link = createTiddlyLink(popup,reminders[t].tiddler,false);\n var diff=reminders[t].diff;\n diff=(!diff)?&quot;Today&quot;:((diff==1)?&quot;Tomorrow&quot;:diff+&quot; days&quot;);\n var txt=(reminders[t].params[&quot;title&quot;])?reminders[t].params[&quot;title&quot;]:reminders[t].tiddler;\n link.appendChild(document.createTextNode(indent+diff+&quot; - &quot;+txt));\n createTiddlyElement(popup,&quot;br&quot;,null,null,null);\n }\n if (readOnly) return; // omit &quot;new reminder...&quot; link\n var link = createTiddlyLink(popup,indent+&quot;new reminder...&quot;,true); createTiddlyElement(popup,&quot;br&quot;);\n var title = when.formatString(format);\n link.title=&quot;add a reminder to '&quot;+title+&quot;'&quot;;\n link.onclick = function() {\n // show tiddler editor\n story.displayTiddler(null, title, 2, null, null, false, false);\n // find body 'textarea'\n var c =document.getElementById(&quot;tiddler&quot; + title).getElementsByTagName(&quot;*&quot;);\n for (var i=0; i&lt;c.length; i++) if ((c[i].tagName.toLowerCase()==&quot;textarea&quot;) &amp;&amp; (c[i].getAttribute(&quot;edit&quot;)==&quot;text&quot;)) break;\n // append reminder macro to tiddler content\n if (i&lt;c.length) {\n if (store.tiddlerExists(title)) c[i].value+=&quot;\sn&quot;; else c[i].value=&quot;&quot;;\n c[i].value += &quot;&lt;&lt;reminder day:&quot;+when.getDate()+&quot; month:&quot;+(when.getMonth()+1)+&quot; year:&quot;+(when.getFullYear())+' title:&quot;Enter a title&quot; &gt;&gt;';\n }\n };\n}\n//}}}\n</div>
<div tiddler="Debugging AJAX client and server solutions in pure Java - no Venkman" modifier="ashinw" modified="200608041950" created="200608041949" tags="Primer Architecture">''Debugging AJAX client and server solution in pure Java - no Venkman''\nYes, this is worthy of a &quot;what the ...&quot; statement. Bloody impressive indeed. So how is it possible you ask?\n# Java source code gets compiled into byte code which will enable server side execution and debugging\n# The [[GWT Hosted platform]] loads and makes a HTTP request on the [[entry point page|GWT Entry point]]\n# The [[entry point page|GWT Entry point]] declares an entry point [[gwt xml module descriptor|GWT Module]] reference\n# The [[GWT Hosted platform]] loads the descriptor and identifies the entry point classifier to construct and bootstrap with onModuleLoad\n# The [[GWT Hosted platform]] then invokes byte-code in your entry point and steps through instructions. Each time the a [[GWT JSNI]] method is encountered it delegates execution to the javascript engine. The irony is, you can debug Java but not the annotated javascript.\n# All events are intercepted by the [[GWT Hosted platform]] which then dispatches them through the [[GWT Event model]]</div>
<div tiddler="DefaultTiddlers" modifier="AshinWima" modified="200606012148" created="200603060508" tags="">[[Welcome]]</div>
<div tiddler="Done" modifier="SimonBaird" modified="200603070149" created="200603070149" tags="sortByCreated"></div>
<div tiddler="Download" modifier="SimonBaird" modified="200604240149" created="200603090205" tags="">Right click ''[[here|./empty_monkeygtd.html]]'' and Save As...</div>
<div tiddler="EditTemplate" modifier="SimonBaird" modified="200601190941" created="200601131226" tags="">&lt;!---\n| Name:|~TagglyTaggingEditTemplate |\n| Version:|1.1 (12-Jan-2006)|\n| Source:|http://simonbaird.com/mptw/#TagglyTaggingEditTemplate|\n| Purpose:|See TagglyTagging for more info|\n| Requires:|You need the CSS in TagglyTaggingStyles to make it look right|\n---&gt;\n&lt;!--{{{--&gt;\n&lt;div class=&quot;toolbar&quot; macro=&quot;toolbar +saveTiddler closeOthers cancelTiddler deleteTiddler&quot;&gt;&lt;/div&gt;\n&lt;div class=&quot;title&quot; macro=&quot;view title&quot;&gt;&lt;/div&gt;\n&lt;div class=&quot;editLabel&quot;&gt;Title&lt;/div&gt;&lt;div class=&quot;editor&quot; macro=&quot;edit title&quot;&gt;&lt;/div&gt;\n&lt;div class=&quot;editLabel&quot;&gt;Tags&lt;/div&gt;&lt;div class=&quot;editor&quot; macro=&quot;edit tags&quot;&gt;&lt;/div&gt;\n&lt;div class=&quot;editorFooter&quot;&gt;&lt;span macro=&quot;message views.editor.tagPrompt&quot;&gt;&lt;/span&gt;&lt;span macro=&quot;tagChooser&quot;&gt;&lt;/span&gt;&lt;/div&gt;\n&lt;div class=&quot;editor&quot; macro=&quot;edit text&quot;&gt;&lt;/div&gt;\n&lt;br/&gt;\n&lt;!--}}}--&gt;</div>
<div tiddler="Enter GWT" modifier="ashinw" modified="200608041900" created="200608041900" tags="Primer Background">!Enter GWT\nThe first thing I did after downloading GWT, was run up the KitchenSink sample application as a pure html client side application. This will give you a flavor of its visual component library. The next thing I proceeded to do was review the KitchenSink source. To my delight everything was Java. But what transformations occurred between the source which I've just compiled in eclipse, and the generated output I'm playing with in the browser? Not only that, why and how is it possible that I'm able to debug the Java based rendering of the KithenSink app? When you stumble across the answers to these questions you will discover GWTs beauty. I will tell you the answers shortly, but it is only when you review the GWT code itself that you gain an appreciation for how clever the design is.</div>
<div tiddler="ForEachTiddler" modifier="SimonBaird" modified="200603060510" created="200603060510" tags="systemConfig">/***\n|''Name:''|ForEachTiddlerPlugin|\n|''Version:''|1.0.5 (2006-02-05)|\n|''Source:''|http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin|\n|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|\n|''Licence:''|[[BSD open source license]]|\n|''Macros:''|[[ForEachTiddlerMacro]] v1.0.5|\n|''TiddlyWiki:''|1.2.38+, 2.0|\n|''Browser:''|Firefox 1.0.4+; Firefox 1.5; InternetExplorer 6.0|\n!Description\n\nCreate customizable lists, tables etc. for your selections of tiddlers. Specify the tiddlers to include and their order through a powerful language.\n\n''Syntax:'' \n|&gt;|{{{&lt;&lt;}}}''forEachTiddler'' [''in'' //tiddlyWikiPath//] [''where'' //whereCondition//] [''sortBy'' //sortExpression// [''ascending'' //or// ''descending'']] [''script'' //scriptText//] [//action// [//actionParameters//]]{{{&gt;&gt;}}}|\n|//tiddlyWikiPath//|The filepath to the TiddlyWiki the macro should work on. When missing the current TiddlyWiki is used.|\n|//whereCondition//|(quoted) JavaScript boolean expression. May refer to the build-in variables {{{tiddler}}} and {{{context}}}.|\n|//sortExpression//|(quoted) JavaScript expression returning &quot;comparable&quot; objects (using '{{{&lt;}}}','{{{&gt;}}}','{{{==}}}'. May refer to the build-in variables {{{tiddler}}} and {{{context}}}.|\n|//scriptText//|(quoted) JavaScript text. Typically defines JavaScript functions that are called by the various JavaScript expressions (whereClause, sortClause, action arguments,...)|\n|//action//|The action that should be performed on every selected tiddler, in the given order. By default the actions [[addToList|AddToListAction]] and [[write|WriteAction]] are supported. When no action is specified [[addToList|AddToListAction]] is used.|\n|//actionParameters//|(action specific) parameters the action may refer while processing the tiddlers (see action descriptions for details). &lt;&lt;tiddler [[JavaScript in actionParameters]]&gt;&gt;|\n|&gt;|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|\n\nSee details see [[ForEachTiddlerMacro]] and [[ForEachTiddlerExamples]].\n\n!Revision history\n* v1.0.5\n** Pass tiddler containing the macro with wikify, context object also holds reference to tiddler containing the macro (&quot;inTiddler&quot;). Thanks to SimonBaird.\n** Support Firefox 1.5.0.1\n** Internal\n*** Make &quot;JSLint&quot; conform\n*** &quot;Only install once&quot;\n* v1.0.4 (2006-01-06)\n** Support TiddlyWiki 2.0\n* v1.0.3 (2005-12-22)\n** Features: \n*** Write output to a file supports multi-byte environments (Thanks to Bram Chen) \n*** Provide API to access the forEachTiddler functionality directly through JavaScript (see getTiddlers and performMacro)\n** Enhancements:\n*** Improved error messages on InternetExplorer.\n* v1.0.2 (2005-12-10)\n** Features: \n*** context object also holds reference to store (TiddlyWiki)\n** Fixed Bugs: \n*** ForEachTiddler 1.0.1 has broken support on win32 Opera 8.51 (Thanks to BrunoSabin for reporting)\n* v1.0.1 (2005-12-08)\n** Features: \n*** Access tiddlers stored in separated TiddlyWikis through the &quot;in&quot; option. I.e. you are no longer limited to only work on the &quot;current TiddlyWiki&quot;.\n*** Write output to an external file using the &quot;toFile&quot; option of the &quot;write&quot; action. With this option you may write your customized tiddler exports.\n*** Use the &quot;script&quot; section to define &quot;helper&quot; JavaScript functions etc. to be used in the various JavaScript expressions (whereClause, sortClause, action arguments,...).\n*** Access and store context information for the current forEachTiddler invocation (through the build-in &quot;context&quot; object) .\n*** Improved script evaluation (for where/sort clause and write scripts).\n* v1.0.0 (2005-11-20)\n** initial version\n\n!Code\n***/\n//{{{\n\n \n//============================================================================\n//============================================================================\n// ForEachTiddlerPlugin\n//============================================================================\n//============================================================================\n\n// Only install once\nif (!version.extensions.ForEachTiddlerPlugin) {\n\nversion.extensions.ForEachTiddlerPlugin = {major: 1, minor: 0, revision: 5, date: new Date(2006,2,5), source: &quot;http://tiddlywiki.abego-software.de/#ForEachTiddlergPlugin&quot;};\n\n// For backward compatibility with TW 1.2.x\n//\nif (!TiddlyWiki.prototype.forEachTiddler) {\n TiddlyWiki.prototype.forEachTiddler = function(callback) {\n for(var t in this.tiddlers) {\n callback.call(this,t,this.tiddlers[t]);\n }\n };\n}\n\n//============================================================================\n// forEachTiddler Macro\n//============================================================================\n\nversion.extensions.forEachTiddler = {major: 1, minor: 0, revision: 5, date: new Date(2006,2,5), provider: &quot;http://tiddlywiki.abego-software.de&quot;};\n\n// ---------------------------------------------------------------------------\n// Configurations and constants \n// ---------------------------------------------------------------------------\n\nconfig.macros.forEachTiddler = {\n // Standard Properties\n label: &quot;forEachTiddler&quot;,\n prompt: &quot;Perform actions on a (sorted) selection of tiddlers&quot;,\n\n // actions\n actions: {\n addToList: {},\n write: {}\n }\n};\n\n// ---------------------------------------------------------------------------\n// The forEachTiddler Macro Handler \n// ---------------------------------------------------------------------------\n\nconfig.macros.forEachTiddler.getContainingTiddler = function(e) {\n while(e &amp;&amp; !hasClass(e,&quot;tiddler&quot;))\n e = e.parentNode;\n var title = e ? e.getAttribute(&quot;tiddler&quot;) : null; \n return title ? store.getTiddler(title) : null;\n};\n\nconfig.macros.forEachTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler) {\n // config.macros.forEachTiddler.traceMacroCall(place,macroName,params,wikifier,paramString,tiddler);\n\n if (!tiddler) tiddler = config.macros.forEachTiddler.getContainingTiddler(place);\n // --- Parsing ------------------------------------------\n\n var i = 0; // index running over the params\n // Parse the &quot;in&quot; clause\n var tiddlyWikiPath = undefined;\n if ((i &lt; params.length) &amp;&amp; params[i] == &quot;in&quot;) {\n i++;\n if (i &gt;= params.length) {\n this.handleError(place, &quot;TiddlyWiki path expected behind 'in'.&quot;);\n return;\n }\n tiddlyWikiPath = this.paramEncode((i &lt; params.length) ? params[i] : &quot;&quot;);\n i++;\n }\n\n // Parse the where clause\n var whereClause =&quot;true&quot;;\n if ((i &lt; params.length) &amp;&amp; params[i] == &quot;where&quot;) {\n i++;\n whereClause = this.paramEncode((i &lt; params.length) ? params[i] : &quot;&quot;);\n i++;\n }\n\n // Parse the sort stuff\n var sortClause = null;\n var sortAscending = true; \n if ((i &lt; params.length) &amp;&amp; params[i] == &quot;sortBy&quot;) {\n i++;\n if (i &gt;= params.length) {\n this.handleError(place, &quot;sortClause missing behind 'sortBy'.&quot;);\n return;\n }\n sortClause = this.paramEncode(params[i]);\n i++;\n\n if ((i &lt; params.length) &amp;&amp; (params[i] == &quot;ascending&quot; || params[i] == &quot;descending&quot;)) {\n sortAscending = params[i] == &quot;ascending&quot;;\n i++;\n }\n }\n\n // Parse the script\n var scriptText = null;\n if ((i &lt; params.length) &amp;&amp; params[i] == &quot;script&quot;) {\n i++;\n scriptText = this.paramEncode((i &lt; params.length) ? params[i] : &quot;&quot;);\n i++;\n }\n\n // Parse the action. \n // When we are already at the end use the default action\n var actionName = &quot;addToList&quot;;\n if (i &lt; params.length) {\n if (!config.macros.forEachTiddler.actions[params[i]]) {\n this.handleError(place, &quot;Unknown action '&quot;+params[i]+&quot;'.&quot;);\n return;\n } else {\n actionName = params[i]; \n i++;\n }\n } \n \n // Get the action parameter\n // (the parsing is done inside the individual action implementation.)\n var actionParameter = params.slice(i);\n\n\n // --- Processing ------------------------------------------\n try {\n this.performMacro({\n place: place, \n inTiddler: tiddler,\n whereClause: whereClause, \n sortClause: sortClause, \n sortAscending: sortAscending, \n actionName: actionName, \n actionParameter: actionParameter, \n scriptText: scriptText, \n tiddlyWikiPath: tiddlyWikiPath});\n\n } catch (e) {\n this.handleError(place, e);\n }\n};\n\n// Returns an object with properties &quot;tiddlers&quot; and &quot;context&quot;.\n// tiddlers holds the (sorted) tiddlers selected by the parameter,\n// context the context of the execution of the macro.\n//\n// The action is not yet performed.\n//\n// @parameter see performMacro\n//\nconfig.macros.forEachTiddler.getTiddlersAndContext = function(parameter) {\n\n var context = config.macros.forEachTiddler.createContext(parameter.place, parameter.whereClause, parameter.sortClause, parameter.sortAscending, parameter.actionName, parameter.actionParameter, parameter.scriptText, parameter.tiddlyWikiPath, parameter.inTiddler);\n\n var tiddlyWiki = parameter.tiddlyWikiPath ? this.loadTiddlyWiki(parameter.tiddlyWikiPath) : store;\n context[&quot;tiddlyWiki&quot;] = tiddlyWiki;\n \n // Get the tiddlers, as defined by the whereClause\n var tiddlers = this.findTiddlers(parameter.whereClause, context, tiddlyWiki);\n context[&quot;tiddlers&quot;] = tiddlers;\n\n // Sort the tiddlers, when sorting is required.\n if (parameter.sortClause) {\n this.sortTiddlers(tiddlers, parameter.sortClause, parameter.sortAscending, context);\n }\n\n return {tiddlers: tiddlers, context: context};\n};\n\n// Returns the (sorted) tiddlers selected by the parameter.\n//\n// The action is not yet performed.\n//\n// @parameter see performMacro\n//\nconfig.macros.forEachTiddler.getTiddlers = function(parameter) {\n return this.getTiddlersAndContext(parameter).tiddlers;\n};\n\n// Performs the macros with the given parameter.\n//\n// @param parameter holds the parameter of the macro as separate properties.\n// The following properties are supported:\n//\n// place\n// whereClause\n// sortClause\n// sortAscending\n// actionName\n// actionParameter\n// scriptText\n// tiddlyWikiPath\n//\n// All properties are optional. \n// For most actions the place property must be defined.\n//\nconfig.macros.forEachTiddler.performMacro = function(parameter) {\n var tiddlersAndContext = this.getTiddlersAndContext(parameter);\n\n // Perform the action\n var actionName = parameter.actionName ? parameter.actionName : &quot;addToList&quot;;\n var action = config.macros.forEachTiddler.actions[actionName];\n if (!action) {\n this.handleError(parameter.place, &quot;Unknown action '&quot;+actionName+&quot;'.&quot;);\n return;\n }\n\n var actionHandler = action.handler;\n actionHandler(parameter.place, tiddlersAndContext.tiddlers, parameter.actionParameter, tiddlersAndContext.context);\n};\n\n// ---------------------------------------------------------------------------\n// The actions \n// ---------------------------------------------------------------------------\n\n// Internal.\n//\n// --- The addToList Action -----------------------------------------------\n//\nconfig.macros.forEachTiddler.actions.addToList.handler = function(place, tiddlers, parameter, context) {\n // Parse the parameter\n var p = 0;\n\n // Check for extra parameters\n if (parameter.length &gt; p) {\n config.macros.forEachTiddler.createExtraParameterErrorElement(place, &quot;addToList&quot;, parameter, p);\n return;\n }\n\n // Perform the action.\n var list = document.createElement(&quot;ul&quot;);\n place.appendChild(list);\n for (var i = 0; i &lt; tiddlers.length; i++) {\n var tiddler = tiddlers[i];\n var listItem = document.createElement(&quot;li&quot;);\n list.appendChild(listItem);\n createTiddlyLink(listItem, tiddler.title, true);\n }\n};\n\n// Internal.\n//\n// --- The write Action ---------------------------------------------------\n//\nconfig.macros.forEachTiddler.actions.write.handler = function(place, tiddlers, parameter, context) {\n // Parse the parameter\n var p = 0;\n if (p &gt;= parameter.length) {\n this.handleError(place, &quot;Missing expression behind 'write'.&quot;);\n return;\n }\n\n var textExpression = config.macros.forEachTiddler.paramEncode(parameter[p]);\n p++;\n\n // Parse the &quot;toFile&quot; option\n var filename = null;\n var lineSeparator = undefined;\n if ((p &lt; parameter.length) &amp;&amp; parameter[p] == &quot;toFile&quot;) {\n p++;\n if (p &gt;= parameter.length) {\n this.handleError(place, &quot;Filename expected behind 'toFile' of 'write' action.&quot;);\n return;\n }\n \n filename = config.macros.forEachTiddler.getLocalPath(config.macros.forEachTiddler.paramEncode(parameter[p]));\n p++;\n if ((p &lt; parameter.length) &amp;&amp; parameter[p] == &quot;withLineSeparator&quot;) {\n p++;\n if (p &gt;= parameter.length) {\n this.handleError(place, &quot;Line separator text expected behind 'withLineSeparator' of 'write' action.&quot;);\n return;\n }\n lineSeparator = config.macros.forEachTiddler.paramEncode(parameter[p]);\n p++;\n }\n }\n \n // Check for extra parameters\n if (parameter.length &gt; p) {\n config.macros.forEachTiddler.createExtraParameterErrorElement(place, &quot;write&quot;, parameter, p);\n return;\n }\n\n // Perform the action.\n var func = config.macros.forEachTiddler.getEvalTiddlerFunction(textExpression, context);\n var count = tiddlers.length;\n var text = &quot;&quot;;\n for (var i = 0; i &lt; count; i++) {\n var tiddler = tiddlers[i];\n text += func(tiddler, context, count, i);\n }\n \n if (filename) {\n if (lineSeparator !== undefined) {\n lineSeparator = lineSeparator.replace(/\s\sn/mg, &quot;\sn&quot;).replace(/\s\sr/mg, &quot;\sr&quot;);\n text = text.replace(/\sn/mg,lineSeparator);\n }\n saveFile(filename, convertUnicodeToUTF8(text));\n } else {\n var wrapper = createTiddlyElement(place, &quot;span&quot;);\n wikify(text, wrapper, null/* highlightRegExp */, context.inTiddler);\n }\n};\n\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n// Internal.\n//\nconfig.macros.forEachTiddler.createContext = function(placeParam, whereClauseParam, sortClauseParam, sortAscendingParam, actionNameParam, actionParameterParam, scriptText, tiddlyWikiPathParam, inTiddlerParam) {\n return {\n place : placeParam, \n whereClause : whereClauseParam, \n sortClause : sortClauseParam, \n sortAscending : sortAscendingParam, \n script : scriptText,\n actionName : actionNameParam, \n actionParameter : actionParameterParam,\n tiddlyWikiPath : tiddlyWikiPathParam,\n inTiddler : inTiddlerParam\n };\n};\n\n// Internal.\n//\n// Returns a TiddlyWiki with the tiddlers loaded from the TiddlyWiki of \n// the given path.\n//\nconfig.macros.forEachTiddler.loadTiddlyWiki = function(path, idPrefix) {\n if (!idPrefix) {\n idPrefix = &quot;store&quot;;\n }\n var lenPrefix = idPrefix.length;\n \n // Read the content of the given file\n var content = loadFile(this.getLocalPath(path));\n if(content === null) {\n throw &quot;TiddlyWiki '&quot;+path+&quot;' not found.&quot;;\n }\n \n // Locate the storeArea div's\n var posOpeningDiv = content.indexOf(startSaveArea);\n var posClosingDiv = content.lastIndexOf(endSaveArea);\n if((posOpeningDiv == -1) || (posClosingDiv == -1)) {\n throw &quot;File '&quot;+path+&quot;' is not a TiddlyWiki.&quot;;\n }\n var storageText = content.substr(posOpeningDiv + startSaveArea.length, posClosingDiv);\n \n // Create a &quot;div&quot; element that contains the storage text\n var myStorageDiv = document.createElement(&quot;div&quot;);\n myStorageDiv.innerHTML = storageText;\n myStorageDiv.normalize();\n \n // Create all tiddlers in a new TiddlyWiki\n // (following code is modified copy of TiddlyWiki.prototype.loadFromDiv)\n var tiddlyWiki = new TiddlyWiki();\n var store = myStorageDiv.childNodes;\n for(var t = 0; t &lt; store.length; t++) {\n var e = store[t];\n var title = null;\n if(e.getAttribute)\n title = e.getAttribute(&quot;tiddler&quot;);\n if(!title &amp;&amp; e.id &amp;&amp; e.id.substr(0,lenPrefix) == idPrefix)\n title = e.id.substr(lenPrefix);\n if(title &amp;&amp; title !== &quot;&quot;) {\n var tiddler = tiddlyWiki.createTiddler(title);\n tiddler.loadFromDiv(e,title);\n }\n }\n tiddlyWiki.dirty = false;\n\n return tiddlyWiki;\n};\n\n\n \n// Internal.\n//\n// Returns a function that has a function body returning the given javaScriptExpression.\n// The function has the parameters:\n// \n// (tiddler, context, count, index)\n//\nconfig.macros.forEachTiddler.getEvalTiddlerFunction = function (javaScriptExpression, context) {\n var script = context[&quot;script&quot;];\n var functionText = &quot;var theFunction = function(tiddler, context, count, index) { return &quot;+javaScriptExpression+&quot;}&quot;;\n var fullText = (script ? script+&quot;;&quot; : &quot;&quot;)+functionText+&quot;;theFunction;&quot;;\n return eval(fullText);\n};\n\n// Internal.\n//\nconfig.macros.forEachTiddler.findTiddlers = function(whereClause, context, tiddlyWiki) {\n var result = [];\n var func = config.macros.forEachTiddler.getEvalTiddlerFunction(whereClause, context);\n tiddlyWiki.forEachTiddler(function(title,tiddler) {\n if (func(tiddler, context, undefined, undefined)) {\n result.push(tiddler);\n }\n });\n return result;\n};\n\n// Internal.\n//\nconfig.macros.forEachTiddler.createExtraParameterErrorElement = function(place, actionName, parameter, firstUnusedIndex) {\n var message = &quot;Extra parameter behind '&quot;+actionName+&quot;':&quot;;\n for (var i = firstUnusedIndex; i &lt; parameter.length; i++) {\n message += &quot; &quot;+parameter[i];\n }\n this.handleError(place, message);\n};\n\n// Internal.\n//\nconfig.macros.forEachTiddler.sortAscending = function(tiddlerA, tiddlerB) {\n var result = \n (tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue) \n ? 0\n : (tiddlerA.forEachTiddlerSortValue &lt; tiddlerB.forEachTiddlerSortValue)\n ? -1 \n : +1; \n return result;\n};\n\n// Internal.\n//\nconfig.macros.forEachTiddler.sortDescending = function(tiddlerA, tiddlerB) {\n var result = \n (tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue) \n ? 0\n : (tiddlerA.forEachTiddlerSortValue &lt; tiddlerB.forEachTiddlerSortValue)\n ? +1 \n : -1; \n return result;\n};\n\n// Internal.\n//\nconfig.macros.forEachTiddler.sortTiddlers = function(tiddlers, sortClause, ascending, context) {\n // To avoid evaluating the sortClause whenever two items are compared \n // we pre-calculate the sortValue for every item in the array and store it in a \n // temporary property (&quot;forEachTiddlerSortValue&quot;) of the tiddlers.\n var func = config.macros.forEachTiddler.getEvalTiddlerFunction(sortClause, context);\n var count = tiddlers.length;\n var i;\n for (i = 0; i &lt; count; i++) {\n var tiddler = tiddlers[i];\n tiddler.forEachTiddlerSortValue = func(tiddler,context, undefined, undefined);\n }\n\n // Do the sorting\n tiddlers.sort(ascending ? this.sortAscending : this.sortDescending);\n\n // Delete the temporary property that holds the sortValue. \n for (i = 0; i &lt; tiddlers.length; i++) {\n delete tiddlers[i].forEachTiddlerSortValue;\n }\n};\n\n\n// Internal.\n//\nconfig.macros.forEachTiddler.trace = function(message) {\n displayMessage(message);\n};\n\n// Internal.\n//\nconfig.macros.forEachTiddler.traceMacroCall = function(place,macroName,params) {\n var message =&quot;&lt;&lt;&quot;+macroName;\n for (var i = 0; i &lt; params.length; i++) {\n message += &quot; &quot;+params[i];\n }\n message += &quot;&gt;&gt;&quot;;\n displayMessage(message);\n};\n\n\n// Internal.\n//\n// Creates an element that holds an error message\n// \nconfig.macros.forEachTiddler.createErrorElement = function(place, exception) {\n var message = (exception.description) ? exception.description : exception.toString();\n return createTiddlyElement(place,&quot;span&quot;,null,&quot;forEachTiddlerError&quot;,&quot;&lt;&lt;forEachTiddler ...&gt;&gt;: &quot;+message);\n};\n\n// Internal.\n//\n// @param place [may be null]\n//\nconfig.macros.forEachTiddler.handleError = function(place, exception) {\n if (place) {\n this.createErrorElement(place, exception);\n } else {\n throw exception;\n }\n};\n\n// Internal.\n//\n// Encodes the given string.\n//\n// Replaces \n// &quot;$))&quot; to &quot;&gt;&gt;&quot;\n// &quot;$)&quot; to &quot;&gt;&quot;\n//\nconfig.macros.forEachTiddler.paramEncode = function(s) {\n var reGTGT = new RegExp(&quot;\s\s$\s\s)\s\s)&quot;,&quot;mg&quot;);\n var reGT = new RegExp(&quot;\s\s$\s\s)&quot;,&quot;mg&quot;);\n return s.replace(reGTGT, &quot;&gt;&gt;&quot;).replace(reGT, &quot;&gt;&quot;);\n};\n\n// Internal.\n//\n// Returns the given original path (that is a file path, starting with &quot;file:&quot;)\n// as a path to a local file, in the systems native file format.\n//\n// Location information in the originalPath (i.e. the &quot;#&quot; and stuff following)\n// is stripped.\n// \nconfig.macros.forEachTiddler.getLocalPath = function(originalPath) {\n // Remove any location part of the URL\n var hashPos = originalPath.indexOf(&quot;#&quot;);\n if(hashPos != -1)\n originalPath = originalPath.substr(0,hashPos);\n // Convert to a native file format assuming\n // &quot;file:///x:/path/path/path...&quot; - pc local file --&gt; &quot;x:\spath\spath\spath...&quot;\n // &quot;file://///server/share/path/path/path...&quot; - FireFox pc network file --&gt; &quot;\s\sserver\sshare\spath\spath\spath...&quot;\n // &quot;file:///path/path/path...&quot; - mac/unix local file --&gt; &quot;/path/path/path...&quot;\n // &quot;file://server/share/path/path/path...&quot; - pc network file --&gt; &quot;\s\sserver\sshare\spath\spath\spath...&quot;\n var localPath;\n if(originalPath.charAt(9) == &quot;:&quot;) // pc local file\n localPath = unescape(originalPath.substr(8)).replace(new RegExp(&quot;/&quot;,&quot;g&quot;),&quot;\s\s&quot;);\n else if(originalPath.indexOf(&quot;file://///&quot;) === 0) // FireFox pc network file\n localPath = &quot;\s\s\s\s&quot; + unescape(originalPath.substr(10)).replace(new RegExp(&quot;/&quot;,&quot;g&quot;),&quot;\s\s&quot;);\n else if(originalPath.indexOf(&quot;file:///&quot;) === 0) // mac/unix local file\n localPath = unescape(originalPath.substr(7));\n else if(originalPath.indexOf(&quot;file:/&quot;) === 0) // mac/unix local file\n localPath = unescape(originalPath.substr(5));\n else // pc network file\n localPath = &quot;\s\s\s\s&quot; + unescape(originalPath.substr(7)).replace(new RegExp(&quot;/&quot;,&quot;g&quot;),&quot;\s\s&quot;); \n return localPath;\n};\n\n// ---------------------------------------------------------------------------\n// Stylesheet Extensions (may be overridden by local StyleSheet)\n// ---------------------------------------------------------------------------\n//\nsetStylesheet(\n &quot;.forEachTiddlerError{color: #ffffff;background-color: #880000;}&quot;,\n &quot;forEachTiddler&quot;);\n\n//============================================================================\n// End of forEachTiddler Macro\n//============================================================================\n\n\n//============================================================================\n// String.startsWith Function\n//============================================================================\n//\n// Returns true if the string starts with the given prefix, false otherwise.\n//\nversion.extensions[&quot;String.startsWith&quot;] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: &quot;http://tiddlywiki.abego-software.de&quot;};\n//\nString.prototype.startsWith = function(prefix) {\n var n = prefix.length;\n return (this.length &gt;= n) &amp;&amp; (this.slice(0, n) == prefix);\n};\n\n\n\n//============================================================================\n// String.endsWith Function\n//============================================================================\n//\n// Returns true if the string ends with the given suffix, false otherwise.\n//\nversion.extensions[&quot;String.endsWith&quot;] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: &quot;http://tiddlywiki.abego-software.de&quot;};\n//\nString.prototype.endsWith = function(suffix) {\n var n = suffix.length;\n return (this.length &gt;= n) &amp;&amp; (this.right(n) == suffix);\n};\n\n\n//============================================================================\n// String.contains Function\n//============================================================================\n//\n// Returns true when the string contains the given substring, false otherwise.\n//\nversion.extensions[&quot;String.contains&quot;] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: &quot;http://tiddlywiki.abego-software.de&quot;};\n//\nString.prototype.contains = function(substring) {\n return this.indexOf(substring) &gt;= 0;\n};\n\n//============================================================================\n// Array.indexOf Function\n//============================================================================\n//\n// Returns the index of the first occurance of the given item in the array or \n// -1 when no such item exists.\n//\n// @param item [may be null]\n//\nversion.extensions[&quot;Array.indexOf&quot;] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: &quot;http://tiddlywiki.abego-software.de&quot;};\n//\nArray.prototype.indexOf = function(item) {\n for (var i = 0; i &lt; this.length; i++) {\n if (this[i] == item) {\n return i;\n }\n }\n return -1;\n};\n\n//============================================================================\n// Array.contains Function\n//============================================================================\n//\n// Returns true when the array contains the given item, otherwise false. \n//\n// @param item [may be null]\n//\nversion.extensions[&quot;Array.contains&quot;] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: &quot;http://tiddlywiki.abego-software.de&quot;};\n//\nArray.prototype.contains = function(item) {\n return (this.indexOf(item) &gt;= 0);\n};\n\n//============================================================================\n// Array.containsAny Function\n//============================================================================\n//\n// Returns true when the array contains at least one of the elements \n// of the item. Otherwise (or when items contains no elements) false is returned.\n//\nversion.extensions[&quot;Array.containsAny&quot;] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: &quot;http://tiddlywiki.abego-software.de&quot;};\n//\nArray.prototype.containsAny = function(items) {\n for(var i = 0; i &lt; items.length; i++) {\n if (this.contains(items[i])) {\n return true;\n }\n }\n return false;\n};\n\n\n//============================================================================\n// Array.containsAll Function\n//============================================================================\n//\n// Returns true when the array contains all the items, otherwise false.\n// \n// When items is null false is returned (even if the array contains a null).\n//\n// @param items [may be null] \n//\nversion.extensions[&quot;Array.containsAll&quot;] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: &quot;http://tiddlywiki.abego-software.de&quot;};\n//\nArray.prototype.containsAll = function(items) {\n for(var i = 0; i &lt; items.length; i++) {\n if (!this.contains(items[i])) {\n return false;\n }\n }\n return true;\n};\n\n\n} // of &quot;install only once&quot;\n\n// Used Globals (for JSLint) ==============\n// ... DOM\n/*global document */\n// ... TiddlyWiki Core\n/*global convertUnicodeToUTF8, createTiddlyElement, createTiddlyLink, \n displayMessage, endSaveArea, hasClass, loadFile, saveFile, \n startSaveArea, store, wikify */\n//}}}\n\n\n/***\n!Licence and Copyright\nCopyright (c) abego Software ~GmbH, 2005 ([[www.abego-software.de|http://www.abego-software.de]])\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this\nlist of conditions and the following disclaimer.\n\nRedistributions in binary form must reproduce the above copyright notice, this\nlist of conditions and the following disclaimer in the documentation and/or other\nmaterials provided with the distribution.\n\nNeither the name of abego Software nor the names of its contributors may be\nused to endorse or promote products derived from this software without specific\nprior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot; AND ANY\nEXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\nOF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT\nSHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\nINCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\nTO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\nBUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\nANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH\nDAMAGE.\n***/\n\n</div>
<div tiddler="Frequented sites" modifier="ashinw" modified="200608051928" created="200606012053" tags="Links">[[GWT Official website|http://code.google.com/webtoolkit/]]\n[[GWT Google group|http://groups.google.com/group/Google-Web-Toolkit]]\n\n[[Gems GWT User public website|http://code.google.com/p/gems-gwt-user/]]\n[[Gems GWT User issue registry|http://code.google.com/p/gems-gwt-user/issues/list]]\n[[Gems GWT User svn repository|http://gems-gwt-user.googlecode.com/svn/]]\n\n[[Gems Official website|http://javaongems.org/]]\n[[Gems User issue registry|http://code.google.com/p/javaongems/issues/list]]\n[[Gems User svn repository|http://javaongems.googlecode.com/svn/]]\n\n[[Juls public website|http://code.google.com/p/juls/]]\n[[Juls issue registry|http://code.google.com/p/juls/issues/list]]\n[[Juls svn repository|http://juls.googlecode.com/svn/]]\n\n</div>
<div tiddler="GWT Assets" modifier="ashinw" modified="200608042211" created="200608041903" tags="Primer Architecture">!GWT Assets\n# GWTCompiler - &lt;&lt;tiddler 'GWT Compiler'&gt;&gt;\n# GWT Hosted platform - &lt;&lt;tiddler 'GWT Hosted platform'&gt;&gt;\n# GWT RPC - &lt;&lt;tiddler 'GWT RPC'&gt;&gt;\n# GWT User widget library - &lt;&lt;tiddler 'GWT Widget user library'&gt;&gt;\n# GWT Entry Point - &lt;&lt;tiddler 'GWT Entry point'&gt;&gt;\n# GWT Module - &lt;&lt;tiddler 'GWT Module'&gt;&gt;\n# GWT Event model - &lt;&lt;tiddler 'GWT Event model'&gt;&gt;\n# GWT Emulation - &lt;&lt;tiddler 'GWT Emulation'&gt;&gt;\n# GWT JSNI - &lt;&lt;tiddler 'GWT JSNI'&gt;&gt;</div>
<div tiddler="GWT Compiler" modifier="ashinw" modified="200608042008" created="200608041904" tags="Primer Architecture Proprietary">The GWT Compiler is a proprietary tool built in Java and distributed with GWT under the Apache 2.0 license. What does that mean you ask? Well that means that you cant review the code for the compiler and Google Javascript obfuscator. This is a frustrating during this early adopter period because most compiler errors reported do not give you any clue as to what the root cause of a problem is, or where the problem is. Hence, for a large application codebase, you are pretty much in the dark. I must admit, I have resorted to injecting byte-code into Google classes in attempt to isolate the method that the compiler choked on.</div>
<div tiddler="GWT Emulation" modifier="ashinw" modified="200608041940" created="200608041940" tags="Primer Architecture">GWT emulation is what really separates GWT apart from all other competing solutions in its field. GWT emulation is the Javascript version of the JDK's application runtime kernel. Those developers that are familiar with thee JDK will know that java.lang (the global package), java.util and java.io are key to all evil in Java. These three packages are what I refer to as the Java runtime application kernel. All things built on Java technology establishes a contract with at least these packages. This is essentially how Java achieves its &quot;write once, run anywhere promise&quot;, by switching the underlying runtime implementations for each platform. GWT does a similar thing, only the container that it runs in is not a hosted OS platform, instead it is a Javascript container engine. Therefore, the GWT team have rewritten the three key packages (named emulation) using javascript with no dependency on a real Java runtime environment. To achieve this effectively the GWT team created [[GWT JSNI]], which stands for JavaScript Native Interface. The emulation library is currently very limited. Not all abstractions in these packages are available. Furthermore, java.text is another package that is seems to be needed. The emulation subsystem is likely the area that will impede GWT's rapid evolution. Until it evolves, you need to rely only on basic Java coding. You cannot link with Apache commons lang or any other utilities because they are likely to require features not yet supported via emulation.</div>
<div tiddler="GWT Entry point" modifier="ashinw" modified="200608041928" created="200608041928" tags="Primer Architecture">The GWT entry point has two elements. The first is a module descriptor which enables you to declare the main class to run with a public onModuleLoad() method. Consider this similar to a Java applications main() method. However, before this classifier even gets loaded, there must exist an entry point bootstrap html page. This html page is the initiator of the GWT runtime framework.</div>
<div tiddler="GWT Event model" modifier="ashinw" modified="200608041932" created="200608041932" tags="Primer Architecture">The GWT event model enables developers to write event handlers for the key GWT system events. Those events are Window closure(s), Browser back/forward navigations and mouse and keyboard events. When a Widget is created in GWT, it is represented in javascript as an opaque DOM element. GWT assigns an event sink byte value to each widget based DOM element. Depending on the type of events that a given DOM element is interested in, it turns those bits on or off. Then when an event occurs, the framework seeks the target DOM element, determines if it has registered for that event, and if true dispatches that event event to the element. Sometimes applications need the ability to intercept events before they get to the target. For this scenario, GWT has created the EventPreview concept which lets developers intercept and eat events before getting a chance to get to the destination.</div>
<div tiddler="GWT Hosted platform" modifier="ashinw" modified="200608042007" created="200608041906" tags="Primer Architecture Proprietary">The GWT hosted platform is a proprietary tool that is a semi Java/SWT/Tomcat application and server with shared object hooks to drive an internal instance of Internet Explorer on the Windows distribution, and I imagine an internal instance of Mozilla on the Linux distribution. This hosted platform's purpose is to enable developers to run and debug their GWT applications in pure Java mode. The embedded Tomcat server is only used if the GWT application has registered some RPC services that the client depends on. Whilst this platform provides the ability to debug the client and server, you will quickly reach its limitations of not being able to leverage other aspects of the Java servlet/JSP specification.\n\nThe hosted platform can be made to work in two modes:\n# &lt;&lt;tiddler 'Hosted mode'&gt;&gt;\n# &lt;&lt;tiddler 'Semi-Hosted mode'&gt;&gt;\n\n</div>
<div tiddler="GWT JSNI" modifier="ashinw" modified="200608041943" created="200608041943" tags="Primer Architecture">GWT JSNI is one of the cleverest tricks I've seen in Java for a long time. JSNI is based on the same underlying contracts of Java Native Interface (JNI). This technique enables method implementations to be tagged as native which means that there implementation is deferred and resolved at runtime through a native platform specific shared object facility. The GWT JSNI design doesn't load any linked library, instead it injects the native javascript for a given method using an annotation like approach. This piece of engineering brilliance when coupled with the emulation subsystem and hosted platform makes GWT complete. Of all the things GWT is, it is these three aggregates that almost made me buy Google stock.</div>
<div tiddler="GWT Module" modifier="ashinw" modified="200608041929" created="200608041928" tags="Primer Architecture">A GWT module is defined by the existence of a gwt xml file. Modules can be inherited (I think 'uses' would have been a better xml tag) to enable one module to use facilities available in another. However, the most interesting aspect of modules is to be able to dynamically replace class implementations based on a compile-time/runtime conditions. This clever design enables engineers to develop solutions that execute different implementations based on say 'user-agent' to achieve the same user experience.\n</div>
<div tiddler="GWT RPC" modifier="ashinw" modified="200608041924" created="200608041924" tags="Primer Architecture">The GWT engineers have probably engineered the simplest and yet functional RPC implementation that I have worked with. Yes, I have paid my dues doing time with CORBA, COM/DOM/COM+, EJB, Oracle Java stored procedures, etc. There are limitations though. GWT RPC will only work with Javascript clients and a Java servlet container. RPC implementations must be written to be thread safe but need not be stateless. Implementers have full access to the HttpSession and therefore can embedd state there. If Java is not your back end, then your only other options are to use either JSON RPC and/or XML RPC.</div>
<div tiddler="GWT Widget user library" modifier="ashinw" modified="200608041925" created="200608041925" tags="Primer Architecture">The GWT widget library is the bundle that hosts all stock standard widgets that ship with GWT. You would have experienced most of those widgets in the KitchSink example. The widget library is similar to most other visual component libraries that targeted desktop application development. For those familiar with AWT/Swing/Delphi/VB/PowerBuilder etc, you will take some comfort in this library. However, from a component developer perspective the game has changed significantly. There is is no primitive canvas a such that you paint your widget on. Instead you assemble your widget by styling various DOM elements to complete a DOM representation of your component.\n</div>
<div tiddler="GWT primer for Gem'ers" modifier="ashinw" modified="200608042002" created="200608041543" tags="Primer">&lt;&lt;tiddler 'Background - Look around one last time before you jump'&gt;&gt;\n&lt;&lt;tiddler 'Enter GWT'&gt;&gt;\n&lt;&lt;tiddler 'GWT Assets'&gt;&gt;\n&lt;&lt;tiddler 'Java source code transformations'&gt;&gt;\n&lt;&lt;tiddler 'Debugging AJAX client and server solutions in pure Java - no Venkman'&gt;&gt;</div>
<div tiddler="Gems-GWT-User project roadmap" modifier="ashinw" modified="200608051844" created="200606011957" tags="Project userlib">The purpose of Gems-gwt-user distribution is to:\n# enable the Gems framework to satisfy its objectives\n# address defects that effect the Gems framework or Gems/Gwt based applications\n\nHence, the gems-gwt-user distribution is driven more by the Gems framework than the official GWT distribution itself. Indeed with every release of GWT, there will be a new release of gems-gwt-user.</div>
<div tiddler="GettingStarted" modifier="SimonBaird" modified="200602250046" created="200602101412" tags="">Welcome to your brand new [[MonkeyPirateTiddlyWiki|http://simonbaird.com/mptw/]]. This is the standard empty [[TiddlyWiki|http://www.tiddlywiki.com/]] (version &lt;&lt;version&gt;&gt;) preconfigured with a few bits and pieces from MPTW, in particular the layout, the colours, and the popular [[TagglyTagging|http://simonbaird.com/mptw/#TagglyTagging]]. If you're new to ~TagglyTagging then try the (slightly out-of-date) [[FAQ|http://simonbaird.com/mptw1/#TagglyTaggingFAQ]] and [[Tutorial|http://simonbaird.com/mptw1/#TagglyTaggingTutorial]].\n\nTo get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:\n* SiteTitle &amp; SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)\n* MainMenu: The menu (usually on the left)\n* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened\nYou'll also need to enter your username for signing your edits: &lt;&lt;option txtUserName&gt;&gt;\n\nTo create your own tiddlers, click 'new tiddler' in the right sidebar. To edit a tiddler click the 'edit' button in the tiddler's toolbar. To save all your tiddlers click 'save changes' in the right sidebar. If you're new to TiddlyWiki check out the formatting info [[here|http://www.tiddlywiki.com/#MainFeatures]].\n\nUse this to import tiddlers from another TiddlyWiki. You can use a local file (click Browse...) or type the url of an online TiddlyWiki.\n&lt;&lt;importTiddlers inline&gt;&gt;\nTo change your colour scheme you can edit the styles in StyleSheet. (Refer to StyleSheetColors and StyleSheetLayout for all styles used).\n\n</div>
<div tiddler="Hosted mode" modifier="ashinw" modified="200608041907" created="200608041907" tags="Primer Architecture">Hosted mode means running an application using the embedded Tomcat server by registering the service in the entry point gwt xml file.</div>
<div tiddler="Java source code transformations" modifier="ashinw" modified="200608041946" created="200608041946" tags="Primer Architecture">''Java source code transformations''\n# Java code gets compiled into byte code. This is done for the benefit of compile-time checking of the application and the server side. There are two aspects to GWT - 1. a client side and 2. a server side. Naturally, if you are developing an [[RPC|GWT RPC]] based solution then you will need interfaces classifiers to cross boundaries. However, it is really only these interface classifiers and server side elements whose byte code is leveraged.\n# [[GWT Compiler]] opens the entry point module xml descriptor and opens that classifier\n# [[GWT Compiler]] iterates through the entry point classifiers dependency chain visiting each method node and re-renders it in javascript\n# If the [[GWT Compiler]] encounters a module with replacement overrides, it renders those overrides in the generated code.\n# The [[GWT Compiler]] generates a complete javascript application image for each browser platform that Google GWT supports (mostly the current crop of browsers)</div>
<div tiddler="Latest news" modifier="ashinw" modified="200608040310" created="200606012021" tags="Main News">Welcome to the Gems distribution of the GWT user library.</div>
<div tiddler="Latest news headline" modifier="AshinWima" modified="200606012032" created="200606012028" tags="Main News">&lt;&lt;tiddler 'Latest news'&gt;&gt;</div>
<div tiddler="License" modifier="ashinw" modified="200608041958" created="200608041957" tags="">/**\n * Copyright 2000 gworks.com.au\n *\n * Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);\n * you may not use this file except in compliance with the License. (http://www.apache.org/licenses/LICENSE-2.0)\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed \n * on an &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for \n * the specific language governing permissions and limitations under the License.\n *\n * &lt;dhamma-message src=&quot;Atisha (11th century Tibetan Buddhist master)&quot;&gt;\n * 	The greatest achievement is selflessness. \n *      The greatest worth is self-mastery.\n * 	The greatest quality is seeking to serve others. \n *      The greatest precept is continual awareness.\n * 	The greatest medicine is the emptiness of everything. \n *      The greatest action is not conforming with the worlds ways.\n * 	The greatest magic is transmuting the passions. \n *      The greatest generosity is non-attachment.\n * 	The greatest goodness is a peaceful mind. \n *      The greatest patience is humility.\n * 	The greatest effort is not concerned with results. \n *      The greatest meditation is a mind that lets go.\n * 	The greatest wisdom is seeing through appearances. \n * &lt;/dhamma-message&gt;\n *\n * @author Ashin Wimalajeewa (ash)\n */\n</div>
<div tiddler="MainMenu" modifier="ashinw" modified="200608041956" created="200603060504" tags="">[[Welcome]] \n[[GWT primer for Gem'ers]]\n[[Reminders|Reminder]]\n----\n[[Links|Aggregated links]]\n[[License]]</div>
<div tiddler="NestedSliders" modifier="SimonBaird" modified="200603080052" created="200603080052" tags="systemConfig">/***\n''NestedSlidersPlugin for TiddlyWiki version 1.2.x and 2.0''\n^^author: Eric Shulman\nsource: http://www.TiddlyTools.com/#NestedSlidersPlugin\nlicense: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^\n\nQuickly make any tiddler content into an expandable 'slider' panel, without needing to create a separate tiddler to contain the slider content. Optional syntax allows ''default to open'', ''custom button label/tooltip'' and ''automatic blockquote formatting.''\n\nYou can also 'nest' these sliders as deep as you like (see complex nesting example below), so that expandable 'tree-like' hierarchical displays can be created. This is most useful when converting existing in-line text content to create in-line annotations, footnotes, context-sensitive help, or other subordinate information displays.\n\nFor more details, please click on a section headline below:\n++++!!!!![Configuration]&gt;\nDebugging messages for 'lazy sliders' deferred rendering:\n&lt;&lt;option chkDebugLazySliderDefer&gt;&gt; show debugging alert when deferring slider rendering\n&lt;&lt;option chkDebugLazySliderRender&gt;&gt; show debugging alert when deferred slider is actually rendered\n===\n++++!!!!![Usage]&gt;\nWhen installed, this plugin adds new wiki syntax for embedding 'slider' panels directly into tiddler content. Use {{{+++}}} and {{{===}}} to delimit the slider content. Additional optional syntax elements let you specify\n*default to open\n*cookiename\n*heading level\n*floater\n*rollover\n*custom label/tooltip\n*automatic blockquote\n*deferred rendering\nThe complete syntax, using all options, is:\n//{{{\n++++(cookiename)!!!!!^*[label|tooltip]&gt;...\ncontent goes here\n===\n//}}}\nwhere:\n* {{{+++}}} (or {{{++++}}}) and {{{===}}}^^\nmarks the start and end of the slider definition, respectively. When the extra {{{+}}} is used, the slider will be open when initially displayed.^^\n* {{{(cookiename)}}}^^\nsaves the slider opened/closed state, and restores this state whenever the slider is re-rendered.^^\n* {{{!}}} through {{{!!!!!}}}^^\ndisplays the slider label using a formatted headline (Hn) style instead of a button/link style^^\n* {{{&quot;^&quot;}}} //(without the quotes)//^^\nmakes the slider 'float' on top of other content rather than shifting that content downward^^\n* {{{&quot;*&quot;}}} //(without the quotes)//^^\nautomatically opens/closes slider on &quot;rollover&quot; as well as when clicked^^\n* {{{[label]}}} or {{{[label|tooltip]}}}^^\nuses custom label/tooltip. (defaults are: &quot;&gt;&quot; (more) and &quot;&lt;&quot; (less)^^\n* {{{&quot;&gt;&quot;}}} //(without the quotes)//^^\nautomatically adds blockquote formatting to slider content^^\n* {{{&quot;...&quot;}}} //(without the quotes)//^^\ndefers rendering of closed sliders until the first time they are opened. //Note: deferred rendering may produce unexpected results in some cases. Use with care.//^^\n\n//Note: to make slider definitions easier to read and recognize when editing a tiddler, newlines immediately following the {{{+++}}} 'start slider' or preceding the {{{===}}} 'end slider' sequence are automatically supressed so that excess whitespace is eliminated from the output.//\n===\n++++!!!!![Examples]&gt;\nsimple in-line slider: \n{{{\n+++\n content\n===\n}}}\n+++\n content\n===\n----\nuse a custom label and tooltip: \n{{{\n+++[label|tooltip]\n content\n===\n}}}\n+++[label|tooltip]\n content\n===\n----\ncontent automatically blockquoted: \n{{{\n+++&gt;\n content\n===\n}}}\n+++&gt;\n content\n===\n----\nall options combined //(default open, cookie, heading, floater, rollover, label/tooltip, blockquoted, deferred)//\n{{{\n++++(testcookie)!!!^*[label|tooltip]&gt;...\n content\n===\n}}}\n++++(testcookie)!!!^*[label|tooltip]&gt;...\n content\n===\n----\ncomplex nesting example:\n{{{\n+++^[get info...|click for information]\n put some general information here, plus a floating slider with more specific info:\n +++^[view details...|click for details]\n put some detail here, which could include a rollover with a +++^*[glossary definition]explaining technical terms===\n ===\n===\n}}}\n+++^[get info...|click for information]\n put some general information here, plus a floating slider with more specific info:\n +++^[view details...|click for details]\n put some detail here, which could include a rollover with a +++^*[glossary definition]explaining technical terms===\n ===\n===\n----\nnested floaters\n&gt;menu: &lt;&lt;tiddler NestedSlidersExample&gt;&gt;\n(see [[NestedSlidersExample]] for definition)\n----\n===\n+++!!!!![Installation]&gt;\nimport (or copy/paste) the following tiddlers into your document:\n''NestedSlidersPlugin'' (tagged with &lt;&lt;tag systemConfig&gt;&gt;)\n===\n+++!!!!![Revision History]&gt;\n\n++++[2006.02.16 - 1.7.7]\ncorrected deferred rendering to account for use-case where show/hide state is tracked in a cookie\n===\n\n++++[2006.02.15 - 1.7.6]\nin adjustSliderPos(), ensure that floating panel is positioned completely within the browser window (i.e., does not go beyond the right edge of the browser window)\n===\n\n++++[2006.02.04 - 1.7.5]\nadd 'var' to unintended global variable declarations to avoid FireFox 1.5.0.1 crash bug when assigning to globals\n===\n\n++++[2006.01.18 - 1.7.4]\nonly define adjustSliderPos() function if it has not already been provided by another plugin. This lets other plugins 'hijack' the function even when they are loaded first.\n===\n\n++++[2006.01.16 - 1.7.3]\nadded adjustSliderPos(place,btn,panel,panelClass) function to permit specialized logic for placement of floating panels. While it provides improved placement for many uses of floating panels, it exhibits a relative offset positioning error when used within *nested* floating panels. Short-term workaround is to only adjust the position for 'top-level' floaters.\n===\n\n++++[2006.01.16 - 1.7.2]\nadded button property to slider panel elements so that slider panel can tell which button it belongs to. Also, re-activated and corrected animation handling so that nested sliders aren't clipped by hijacking Slider.prototype.stop so that &quot;overflow:hidden&quot; can be reset to &quot;overflow:visible&quot; after animation ends\n===\n\n++++[2006.01.14 - 1.7.1]\nadded optional &quot;^&quot; syntax for floating panels. Defines new CSS class, &quot;.floatingPanel&quot;, as an alternative for standard in-line &quot;.sliderPanel&quot; styles.\n===\n\n++++[2006.01.14 - 1.7.0]\nadded optional &quot;*&quot; syntax for rollover handling to show/hide slider without requiring a click (Based on a suggestion by tw4efl)\n===\n\n+++[2006.01.03 - 1.6.2]\nWhen using optional &quot;!&quot; heading style, instead of creating a clickable &quot;Hn&quot; element, create an &quot;A&quot; element inside the &quot;Hn&quot; element. (allows click-through in SlideShowPlugin, which captures nearly all click events, except for hyperlinks)\n===\n\n+++[2005.12.15 - 1.6.1]\nadded optional &quot;...&quot; syntax to invoke deferred ('lazy') rendering for initially hidden sliders\nremoved checkbox option for 'global' application of lazy sliders\n===\n\n+++[2005.11.25 - 1.6.0]\nadded optional handling for 'lazy sliders' (deferred rendering for initially hidden sliders)\n===\n\n+++[2005.11.21 - 1.5.1]\nrevised regular expressions: if present, a single newline //preceding// and/or //following// a slider definition will be suppressed so start/end syntax can be place on separate lines in the tiddler 'source' for improved readability. Similarly, any whitespace (newlines, tabs, spaces, etc.) trailing the 'start slider' syntax or preceding the 'end slider' syntax is also suppressed.\n===\n\n+++[2005.11.20 - 1.5.0]\n added (cookiename) syntax for optional tracking and restoring of slider open/close state\n===\n\n+++[2005.11.11 - 1.4.0]\n added !!!!! syntax to render slider label as a header (Hn) style instead of a button/link style\n===\n\n+++[2005.11.07 - 1.3.0]\n removed alternative syntax {{{(((}}} and {{{)))}}} (so they can be used by other\n formatting extensions) and simplified/improved regular expressions to trim multiple excess newlines\n===\n\n+++[2005.11.05 - 1.2.1]\n changed name to NestedSlidersPlugin\n more documentation\n===\n\n+++[2005.11.04 - 1.2.0]\n added alternative character-mode syntax {{{(((}}} and {{{)))}}}\n tweaked &quot;eat newlines&quot; logic for line-mode {{{+++}}} and {{{===}}} syntax\n===\n\n+++[2005.11.03 - 1.1.1]\n fixed toggling of default tooltips (&quot;more...&quot; and &quot;less...&quot;) when a non-default button label is used\n code cleanup, added documentation\n===\n\n+++[2005.11.03 - 1.1.0]\n changed delimiter syntax from {{{(((}}} and {{{)))}}} to {{{+++}}} and {{{===}}}\n changed name to EasySlidersPlugin\n===\n\n+++[2005.11.03 - 1.0.0]\n initial public release\n===\n\n===\n+++!!!!![Credits]&gt;\nThis feature was implemented by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]] with research, programming and suggestions from RodneyGomes, GeoffSlocock, and PaulPetterson\n===\n***/\n// //+++!!!!![Code]\n//{{{\nversion.extensions.nestedSliders = {major: 1, minor: 7, revision: 7, date: new Date(2006,2,16)};\n//}}}\n\n//{{{\n// options for deferred rendering of sliders that are not initially displayed\nif (config.options.chkDebugLazySliderDefer==undefined) config.options.chkDebugLazySliderDefer=false;\nif (config.options.chkDebugLazySliderRender==undefined) config.options.chkDebugLazySliderRender=false;\n\n// default styles for 'floating' class\nsetStylesheet(&quot;.floatingPanel { position:absolute; z-index:10; padding:0.5em; margin:0em; \s\n background-color:#eee; color:#000; border:1px solid #000; text-align:left; }&quot;,&quot;floatingPanelStylesheet&quot;);\n//}}}\n\n//{{{\nconfig.formatters.push( {\n name: &quot;nestedSliders&quot;,\n match: &quot;\s\sn?\s\s+{3}&quot;,\n terminator: &quot;\s\ss*\s\s={3}\s\sn?&quot;,\n lookahead: &quot;\s\sn?\s\s+{3}(\s\s+)?(\s\s([^\s\s)]*\s\s))?(\s\s!*)?(\s\s^)?(\s\s*)?(\s\s[[^\s\s]]*\s\s])?(\s\s&gt;)?(\s\s.\s\s.\s\s.)?\s\ss*&quot;,\n handler: function(w)\n {\n var lookaheadRegExp = new RegExp(this.lookahead,&quot;mg&quot;);\n lookaheadRegExp.lastIndex = w.matchStart;\n var lookaheadMatch = lookaheadRegExp.exec(w.source)\n if(lookaheadMatch &amp;&amp; lookaheadMatch.index == w.matchStart)\n {\n // location for rendering button and panel\n var place=w.output;\n\n // default to closed, no cookie\n var show=&quot;none&quot;; var title=&quot;&gt;&quot;; var tooltip=&quot;show&quot;; var cookie=&quot;&quot;;\n\n // extra &quot;+&quot;, default to open\n if (lookaheadMatch[1])\n { show=&quot;block&quot;; title=&quot;&lt;&quot;; tooltip=&quot;hide&quot;; }\n\n // cookie, use saved open/closed state\n if (lookaheadMatch[2]) {\n cookie=lookaheadMatch[2].trim().substr(1,lookaheadMatch[2].length-2);\n cookie=&quot;chkSlider&quot;+cookie;\n if (config.options[cookie]==undefined)\n { config.options[cookie] = (show==&quot;block&quot;) }\n if (config.options[cookie])\n { show=&quot;block&quot;; title=&quot;&lt;&quot;; tooltip=&quot;hide&quot;; }\n else\n { show=&quot;none&quot;; title=&quot;&gt;&quot;; tooltip=&quot;show&quot;; }\n }\n\n // custom label/tooltip\n if (lookaheadMatch[6]) {\n title = lookaheadMatch[6].trim().substr(1,lookaheadMatch[6].length-2);\n var pos=title.indexOf(&quot;|&quot;);\n if (pos!=-1)\n { tooltip = title.substr(pos+1,title.length); title = title.substr(0,pos); }\n else\n { tooltip += &quot; &quot;+title; }\n }\n\n // create the button\n if (lookaheadMatch[3]) { // use &quot;Hn&quot; header format instead of button/link\n var lvl=(lookaheadMatch[3].length&gt;6)?6:lookaheadMatch[3].length;\n var btn = createTiddlyElement(createTiddlyElement(place,&quot;h&quot;+lvl,null,null,null),&quot;a&quot;,null,null,title);\n btn.onclick=onClickNestedSlider;\n btn.setAttribute(&quot;href&quot;,&quot;javascript:;&quot;);\n btn.setAttribute(&quot;title&quot;,tooltip);\n }\n else\n var btn = createTiddlyButton(place,title,tooltip,onClickNestedSlider);\n btn.sliderCookie = cookie; // save the cookiename (if any) in the button object\n\n // &quot;non-click&quot; MouseOver open/close slider\n if (lookaheadMatch[5]) btn.onmouseover=onClickNestedSlider;\n\n // create slider panel\n var panelClass=lookaheadMatch[4]?&quot;floatingPanel&quot;:&quot;sliderPanel&quot;;\n var panel=createTiddlyElement(place,&quot;div&quot;,null,panelClass,null);\n panel.style.display = show;\n panel.button = btn; // so the slider panel know which button it belongs to\n btn.sliderPanel=panel;\n\n // render slider (or defer until shown) \n w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;\n if ((show==&quot;block&quot;)||!lookaheadMatch[8]) {\n // render now if panel is supposed to be shown or NOT deferred rendering\n w.subWikify(lookaheadMatch[7]?createTiddlyElement(panel,&quot;blockquote&quot;):panel,this.terminator);\n // align slider/floater position with button\n adjustSliderPos(place,btn,panel,panelClass);\n }\n else {\n var src = w.source.substr(w.nextMatch);\n var endpos=findMatchingDelimiter(src,&quot;+++&quot;,&quot;===&quot;);\n panel.setAttribute(&quot;raw&quot;,src.substr(0,endpos));\n panel.setAttribute(&quot;blockquote&quot;,lookaheadMatch[7]?&quot;true&quot;:&quot;false&quot;);\n panel.setAttribute(&quot;rendered&quot;,&quot;false&quot;);\n w.nextMatch += endpos+3;\n if (w.source.substr(w.nextMatch,1)==&quot;\sn&quot;) w.nextMatch++;\n if (config.options.chkDebugLazySliderDefer) alert(&quot;deferred '&quot;+title+&quot;':\sn\sn&quot;+panel.getAttribute(&quot;raw&quot;));\n }\n }\n }\n }\n)\n\n// TBD: ignore 'quoted' delimiters (e.g., &quot;{{{+++foo===}}}&quot; isn't really a slider)\nfunction findMatchingDelimiter(src,starttext,endtext) {\n var startpos = 0;\n var endpos = src.indexOf(endtext);\n // check for nested delimiters\n while (src.substring(startpos,endpos-1).indexOf(starttext)!=-1) {\n // count number of nested 'starts'\n var startcount=0;\n var temp = src.substring(startpos,endpos-1);\n var pos=temp.indexOf(starttext);\n while (pos!=-1) { startcount++; pos=temp.indexOf(starttext,pos+starttext.length); }\n // set up to check for additional 'starts' after adjusting endpos\n startpos=endpos+endtext.length;\n // find endpos for corresponding number of matching 'ends'\n while (startcount &amp;&amp; endpos!=-1) {\n endpos = src.indexOf(endtext,endpos+endtext.length);\n startcount--;\n }\n }\n return (endpos==-1)?src.length:endpos;\n}\n//}}}\n\n//{{{\nfunction onClickNestedSlider(e)\n{\n if (!e) var e = window.event;\n var theTarget = resolveTarget(e);\n var theLabel = theTarget.firstChild.data;\n var theSlider = theTarget.sliderPanel\n var isOpen = theSlider.style.display!=&quot;none&quot;;\n // if using default button labels, toggle labels\n if (theLabel==&quot;&gt;&quot;) theTarget.firstChild.data = &quot;&lt;&quot;;\n else if (theLabel==&quot;&lt;&quot;) theTarget.firstChild.data = &quot;&gt;&quot;;\n // if using default tooltips, toggle tooltips\n if (theTarget.getAttribute(&quot;title&quot;)==&quot;show&quot;)\n theTarget.setAttribute(&quot;title&quot;,&quot;hide&quot;);\n else if (theTarget.getAttribute(&quot;title&quot;)==&quot;hide&quot;)\n theTarget.setAttribute(&quot;title&quot;,&quot;show&quot;);\n if (theTarget.getAttribute(&quot;title&quot;)==&quot;show &quot;+theLabel)\n theTarget.setAttribute(&quot;title&quot;,&quot;hide &quot;+theLabel);\n else if (theTarget.getAttribute(&quot;title&quot;)==&quot;hide &quot;+theLabel)\n theTarget.setAttribute(&quot;title&quot;,&quot;show &quot;+theLabel);\n // deferred rendering (if needed)\n if (theSlider.getAttribute(&quot;rendered&quot;)==&quot;false&quot;) {\n if (config.options.chkDebugLazySliderRender)\n alert(&quot;rendering '&quot;+theLabel+&quot;':\sn\sn&quot;+theSlider.getAttribute(&quot;raw&quot;));\n var place=theSlider;\n if (theSlider.getAttribute(&quot;blockquote&quot;)==&quot;true&quot;)\n place=createTiddlyElement(place,&quot;blockquote&quot;);\n wikify(theSlider.getAttribute(&quot;raw&quot;),place);\n theSlider.setAttribute(&quot;rendered&quot;,&quot;true&quot;);\n }\n // show/hide the slider\n if(config.options.chkAnimate)\n anim.startAnimating(new Slider(theSlider,!isOpen,e.shiftKey || e.altKey,&quot;none&quot;));\n else\n theSlider.style.display = isOpen ? &quot;none&quot; : &quot;block&quot;;\n if (this.sliderCookie &amp;&amp; this.sliderCookie.length)\n { config.options[this.sliderCookie]=!isOpen; saveOptionCookie(this.sliderCookie); }\n // align slider/floater position with target button\n adjustSliderPos(theSlider.parentNode,theTarget,theSlider,theSlider.className);\n return false;\n}\n\n// hijack animation handler 'stop' handler so overflow is visible after animation has completed\nSlider.prototype.coreStop = Slider.prototype.stop;\nSlider.prototype.stop = function() { this.coreStop(); this.element.style.overflow = &quot;visible&quot;; }\n\n// adjust panel position based on button position\nif (window.adjustSliderPos==undefined) window.adjustSliderPos=function(place,btn,panel,panelClass) {\n ///////////////////////////////////////////////////////////////////////////////\n /// EXPERIMENTAL HACK - WORKS IN SOME CASES, NOT IN OTHERS\n ///////////////////////////////////////////////////////////////////////////////\n // &quot;if this panel is floating and the parent is not also a floating panel&quot;...\n if (panelClass==&quot;floatingPanel&quot; &amp;&amp; place.className!=&quot;floatingPanel&quot;) {\n var left=0; var top=btn.offsetHeight;\n if (place.style.position!=&quot;relative&quot;) { left+=findPosX(btn); top+=findPosY(btn); }\n if (left+panel.offsetWidth &gt; getWindowWidth()) left=getWindowWidth()-panel.offsetWidth-10;\n panel.style.left=left+&quot;px&quot;; panel.style.top=top+&quot;px&quot;;\n }\n}\n\nfunction getWindowWidth() {\n if(document.width!=undefined)\n return document.width; // moz (FF)\n if(document.documentElement &amp;&amp; ( document.documentElement.clientWidth || document.documentElement.clientHeight ) )\n return document.documentElement.clientWidth; // IE6\n if(document.body &amp;&amp; ( document.body.clientWidth || document.body.clientHeight ) )\n return document.body.clientWidth; // IE4\n if(window.innerWidth!=undefined)\n return window.innerWidth; // IE - general\n return 0; // unknown\n}\n//}}}\n// //===</div>
<div tiddler="NewHereCommand" modifier="SimonBaird" modified="200601200055" created="200601061833" tags="systemConfig">/***\n|Name|NewHereCommand|\n|Source|http://simonbaird.com/mptw/#NewHereCommand|\n|Version|1.0|\n\nCode originally by ArphenLin. Small tweak by SimonBaird\nhttp://aiddlywiki.sourceforge.net/NewHere_demo.html#NewHereCommand\nTo use this you must edit your ViewTemplate and add newHere to the toolbar div, eg\n{{{&lt;div class='toolbar' macro='toolbar ... newHere'&gt;&lt;/div&gt;}}}\n***/\n\n//{{{\n\nconfig.commands.newHere = {\n text: 'new here',\n tooltip: 'Create a new tiddler tagged as this tiddler',\n handler: function(e,src,title) {\n if (!readOnly) {\n clearMessage();\n var t=document.getElementById('tiddler'+title);\n story.displayTiddler(t,config.macros.newTiddler.title,DEFAULT_EDIT_TEMPLATE);\n story.setTiddlerTag(config.macros.newTiddler.title, title, 0);\n story.focusTiddler(config.macros.newTiddler.title,&quot;title&quot;);\n return false;\n }\n }\n};\n\n//}}}</div>
<div tiddler="NewerTiddlerPlugin" modifier="SimonBaird" modified="200603100049" created="200603060547" tags="systemConfig">/***\n|''Name:''|NewerTiddlerPlugin|\n|''Version:''|$Revision: 13 $ |\n|''Source:''|http://thePettersons.org/tiddlywiki.html#NewerTiddlerPlugin |\n|''Author:''|[[Paul Petterson]] |\n|''Type:''|Macro Extension |\n|''Requires:''|TiddlyWiki 1.2.33 or higher |\n!Description\nCreate a 'new tiddler' button with lots more options! Specify the text to show on the button, the name of the new tiddler (with date macro expansion), one or more tags for the new tiddlers, and what text if any to include in the new tiddler body! Uses a named parameter format, simalar to the reminder plugin.\n\nAlso - if the tiddler already exists it won't replace any of it's existing data (like tags).\n\n!Syntax\n* {{{&lt;&lt;newerTiddler button:&quot;Inbox&quot; name:&quot;Inbox YYYY/MM/DD&quot; tags:&quot;Journal, inbox&quot; text:&quot;New stuff for today:&quot;&gt;&gt;}}}\n* {{{&lt;&lt;newerTiddler button:&quot;@Action&quot; name:&quot;Action: what&quot; tags:&quot;@Action&quot; text:&quot;Add project and describe action&quot;&gt;&gt;}}}\n* {{{&lt;&lt;newerTiddler button:&quot;New Project&quot; name:&quot;Project Name?&quot; tags:&quot;My Projects, My Inbox, Journal&quot; template:&quot;MyTemplate&quot;&gt;&gt;}}}\n!!Parameters\n* name:&quot;Name of Tiddler&quot;\n* tags:&quot;Tag1, Tag2, Tag3&quot; - tags for new tiddler, comma seperated //don't use square brackets //({{{[[}}})// for tags!//\n* button:&quot;name for button&quot; - the name to display instead of &quot;new tiddler&quot;\n* body:&quot;what to put in the tiddler body&quot;\n* template:&quot;Name of a tiddler containing the text to use as the body of the new tiddler&quot;\n\n''Note:'' if you sepecify both body and template parameters, then template parameter will be used and the body parameter overridden.\n\n!Sample Output\n* &lt;&lt;newerTiddler button:&quot;Inbox&quot; name:&quot;Inbox YYYY/MM/DD&quot; tags:&quot;Journal inbox&quot; text:&quot;New stuff for today:&quot;&gt;&gt;\n* &lt;&lt;newerTiddler button:&quot;@Action&quot; name:&quot;Action: what&quot; tags:&quot;@Action&quot; text:&quot;Add project and describe action&quot;&gt;&gt;\n* &lt;&lt;newerTiddler button:&quot;New Project&quot; name:&quot;Project Name?&quot; tags:&quot;[[My Projects]] [[My Inbox]] Journal&quot; template:&quot;MyTemplate&quot;&gt;&gt;\n\n!Todo\n&lt;&lt;projectTemplate&gt;&gt;\n\n!Known issues\n* Must use double quotes (&quot;) around parameter values if they contain a space, can't use single quotes (').\n* can't use standard bracketted style tags, ust type in the tags space and all and put a comma between them. For example tags:&quot;one big tag, another big tag&quot; uses 2 tags ''one big tag'' and ''another big tag''.\n\n!Notes\n* It works fine, and I use it daily, however I haven't really tested edge cases or multiple platforms. If you run into bugs or problems, let me know!\n\n!Requests\n* Have delta-date specifiers on the name: name:&quot;Inbox YYY/MM/DD+1&quot; ( ceruleat@gmail.com )\n* Option to just open the tiddler instead of immediately edit it ( ceruleat@gmail.com )\n* Have date formatters in tags as well as in name (me)\n\n!Revision history\n$History: PaulsNotepad.html $\n * \n * ***************** Version 2 *****************\n * User: paulpet Date: 2/26/06 Time: 7:25p\n * Updated in $/PaulsNotepad3.0.root/PaulsNotepad3.0/PaulsPlugins/systemConfig\n * Port to tw2.0, bug fixes, and simplification!\nv1.0.2 (not released) - fixed small documentation issues.\nv1.0.1 October 13th - fixed a bug occurring only in FF\nv1.0 October 11th - Initial public release\nv0.8 October 10th - Feature complete... \nv0.7 Initial public preview\n\n!Code\n***/\n//{{{\nconfig.macros.newerTiddler = { \nname:&quot;New(er) Tiddler&quot;,\ntags:&quot;&quot;,\ntext:&quot;Type Tiddler Contents Here.&quot;,\nbutton:&quot;new(er) tiddler&quot;,\n\nreparse: function( params ) {\n var re = /([^:\s'\s&quot;\ss]+)(?::([^\s'\s&quot;:\ss]+)|:[\s'\s&quot;]([^\s'\s&quot;\s\s]*(?:\s\s.[^\s'\s&quot;\s\s]*)*)[\s'\s&quot;])?(?=\ss|$)/g;\n var ret = new Array() ;\n var m ;\n\n while( (m = re.exec( params )) != null )\n ret[ m[1] ] = m[2]?m[2]:m[3]?m[3]:true ;\n\n return ret ;\n},\nhandler: function(place,macroName,params,wikifier,paramString,tiddler) {\n if ( readOnly ) return ;\n\n var input = this.reparse( paramString ) ;\n var tiddlerName = input[&quot;name&quot;]?input[&quot;name&quot;].trim():config.macros.newerTiddler.name ;\n var tiddlerTags = input[&quot;tags&quot;]?input[&quot;tags&quot;]:config.macros.newerTiddler.tags ;\n var tiddlerBody = input[&quot;text&quot;]?input[&quot;text&quot;]:config.macros.newerTiddler.text ;\n var buttonText = input[&quot;button&quot;]?input[&quot;button&quot;]:config.macros.newerTiddler.button ;\n var template = input[&quot;template&quot;]?input[&quot;template&quot;]:null;\n\n // if there is a template, use it - otherwise use the tiddlerBody text\n if ( template ) {\n tiddlerBody = store.getTiddlerText( template );\n }\n if ( tiddlerBody == null || tiddlerBody.length == 0 )\n tiddlerBody = config.macros.newerTiddler.text ;\n\n // mptw hack\n tiddlerBody = tiddlerBody.replace(/\s$\s)\s)/g,&quot;&gt;&gt;&quot;);\n tiddlerBody = tiddlerBody.replace(/\s$\s}\s}/g,&quot;&gt;&gt;&quot;);\n\n var now = new Date() ;\n tiddlerName = now.formatString( tiddlerName ) ;\n \n createTiddlyButton( place, buttonText, &quot;&quot;, function() {\n var exists = store.tiddlerExists( tiddlerName );\n var t = store.createTiddler( tiddlerName );\n if ( ! exists )\n t.assign( tiddlerName, tiddlerBody, config.views.wikified.defaultModifier, now, tiddlerTags.readBracketedList() );\n \n story.displayTiddler(null,tiddlerName,DEFAULT_EDIT_TEMPLATE);\n story.focusTiddler(tiddlerName,&quot;title&quot;);\n return false;\n });\n}}\n//}}}\n/***\nThis plugin is released under the [[Creative Commons Attribution 2.5 License|http://creativecommons.org/licenses/by/2.5/]]\n***/\n\n</div>
<div tiddler="Next" modifier="SimonBaird" modified="200603080248" created="200603080248" tags=""></div>
<div tiddler="Next release of GWT" modifier="ashinw" modified="200608040303" created="200608040244" tags="Wait Task [[Gems-GWT-User project roadmap]]">* check out trunk\n* prepare for overlay by delete all svn meta-data content\n* extract new GWT distribution\n* add register working copy changes\n* build distribution\n* commit to trunk\n* tag new release\n* merge difference between first and second tags to gems branch\n* resolve conflicts\n* build distribution\n* commit to branch\n* issue communications to:\n** GWT group\n** Gems group\n</div>
<div tiddler="PageTemplate" modifier="AshinWima" modified="200606020301" created="200606011801" tags="">&lt;!--{{{--&gt;\n\n&lt;!-- jump:tw-strip:begin --&gt;\n&lt;div class='header' macro='gradient vert #9ADEF5 #152f46'&gt;&lt;div class='headerShadow'&gt;&lt;span class='siteTitle' refresh='content' tiddler='SiteTitle'&gt;&lt;/span&gt;&amp;nbsp;&lt;span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'&gt;&lt;/span&gt;&lt;/div&gt; &lt;div class='headerForeground'&gt;&lt;/div&gt;&lt;/div&gt;\n&lt;!-- jump:tw-strip:end --&gt;\n\n&lt;div id='mainMenu' refresh='content' tiddler='MainMenu'&gt;&lt;/div&gt;\n&lt;div id='sidebar'&gt;\n&lt;div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'&gt;&lt;/div&gt;\n&lt;div id='sidebarCalendar' macro='calendar thismonth'&gt;&lt;/div&gt;\n&lt;div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'&gt;&lt;/div&gt;\n&lt;/div&gt;\n&lt;div id='displayArea'&gt;\n&lt;div id='messageArea'&gt;&lt;/div&gt;\n&lt;div id='tiddlerDisplay'&gt;&lt;/div&gt;\n&lt;/div&gt;\n&lt;!--}}}--&gt;\n</div>
<div tiddler="Priority" modifier="SimonBaird" modified="200604240224" created="200603060510" tags="">You can change what your priorities are by renaming, adding to or removing these tiddlers.</div>
<div tiddler="Project" modifier="ashinw" modified="200608040305" created="200603060609" tags=""></div>
<div tiddler="ProjectViewTemplate" modifier="YourName" modified="200605070809" created="200603080527" tags="ViewTemplates">&lt;!---\n| Name:|ProjectViewTemplate |\n| Version:||\n| Source:|http://simonbaird.com/mptw/|\n---&gt;\n&lt;!--{{{--&gt;\n&lt;div class=&quot;toolbar&quot; macro=&quot;toolbar -closeTiddler closeOthers +editTiddler permalink references jump newHere&quot;&gt;&lt;/div&gt;\n&lt;div class=&quot;tagglyTagged&quot; macro=&quot;hideSomeTags&quot;&gt;&lt;/div&gt;\n&lt;div&gt;&lt;span class=&quot;title&quot; macro=&quot;view title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;miniTag&quot; macro=&quot;miniTag&quot;&gt;&lt;/span&gt;&lt;/div&gt;\n&lt;div class='subtitle'&gt;Created &lt;span macro='view created date [[DD/MM/YY]]'&gt;&lt;/span&gt;, updated &lt;span macro='view modified date [[DD/MM/YY]]'&gt;&lt;/span&gt;&lt;/div&gt;\n&lt;div class=&quot;viewer&quot; macro=&quot;view text wikified&quot;&gt;&lt;/div&gt;\n\n&lt;table width=&quot;100%&quot;&gt;&lt;tr&gt;\n&lt;td valign=&quot;top&quot; style=&quot;font-size:90%;border-right:1px dashed #888;padding:0.5em;&quot;&gt;\n&lt;xmp macro=&quot;wikifyContents&quot; class=&quot;viewer&quot;&gt;\n{div{nextAction{[[Next Actions|Next]] \s\n&lt;&lt;forEachTiddler where 'tiddler.title == &quot;SiteTitle&quot;'\n write '&quot;&lt;&lt;newerTiddler button:\s&quot;new\s&quot; tags:\s&quot;Next Task [[&quot; + \n context.inTiddler.title + \n &quot;]]\s&quot; name:\s&quot;New Task\s&quot; $))&quot;'&gt;&gt; \s\n\s\n&lt;&lt;forEachTiddler\n where 'tiddler.tags.containsAll([&quot;Task&quot;,&quot;Next&quot;,context.inTiddler.title]) &amp;&amp; !tiddler.tags.contains(&quot;Done&quot;)'\n&gt;&gt;}}}\n{div{nextAction{[[Waiting For|Wait]] \s\n&lt;&lt;forEachTiddler where 'tiddler.title == &quot;SiteTitle&quot;'\n write '&quot;&lt;&lt;newerTiddler button:\s&quot;new\s&quot; tags:\s&quot;Wait Task [[&quot; + \n context.inTiddler.title + \n &quot;]]\s&quot; name:\s&quot;New Task\s&quot; $))&quot;'&gt;&gt; \s\n\s\n&lt;&lt;forEachTiddler\n where 'tiddler.tags.containsAll([&quot;Task&quot;,&quot;Wait&quot;,context.inTiddler.title]) &amp;&amp; !tiddler.tags.contains(&quot;Done&quot;)'&gt;&gt;}}}\n&lt;&lt;forEachTiddler where 'tiddler.tags.containsAll([context.inTiddler.title, &quot;Task&quot;]) &amp;&amp; !tiddler.tags.contains(&quot;Done&quot;) &amp;&amp; !tiddler.tags.contains(&quot;Next&quot;) &amp;&amp; !tiddler.tags.contains(&quot;Wait&quot;) &amp;&amp; !tiddler.tags.contains(&quot;Someday&quot;)'\n write\n '&quot;@@font-size:90%;padding-left:0.5em;[[&quot; + tiddler.title + &quot;]]@@ \sn&quot;'\n&gt;&gt;\n----\n[[Someday/Maybe|Someday]] \s\n&lt;&lt;forEachTiddler where 'tiddler.title == &quot;SiteTitle&quot;'\n write '&quot;&lt;&lt;newerTiddler button:\s&quot;new\s&quot; tags:\s&quot;Someday Task [[&quot; + \n context.inTiddler.title + \n &quot;]]\s&quot; name:\s&quot;New Task\s&quot; $))&quot;'&gt;&gt; \s\n\s\n&lt;&lt;forEachTiddler\n where 'tiddler.tags.containsAll([&quot;Task&quot;,&quot;Someday&quot;,context.inTiddler.title]) &amp;&amp; !tiddler.tags.contains(&quot;Done&quot;)'&gt;&gt;\n----\n[[Done]] \s\n{div{scrolling{\s\n&lt;&lt;forEachTiddler\n where 'tiddler.tags.containsAll([&quot;Task&quot;,context.inTiddler.title]) &amp;&amp; tiddler.tags.contains(&quot;Done&quot;)'\n sortBy 'tiddler.modified' descending &gt;&gt;\s\n}}}\n\n&lt;/xmp&gt;\n&lt;/td&gt;\n\n\n&lt;td valign=&quot;top&quot; style=&quot;font-size:90%;padding:0.5em;&quot;&gt;\n&lt;xmp macro=&quot;wikifyContents&quot; class=&quot;viewer&quot;&gt;\n/% ha ha!! better way???? it's like select 'thing' thing from dual %/ \s\n[[Reminders|Reminder]] &lt;&lt;forEachTiddler where 'tiddler.title == &quot;SiteTitle&quot;'\n write '&quot;&lt;&lt;newerTiddler button:\s&quot;new\s&quot; tags:\s&quot;Reminder [[&quot; + \n context.inTiddler.title + \n &quot;]]\s&quot; name:\s&quot;New Reminder\s&quot; text:\s&quot;&lt;&lt;newReminder$}}\s&quot;$))&quot;'&gt;&gt;++++\n&lt;&lt;forEachTiddler where 'tiddler.title == &quot;SiteTitle&quot;' write\n '&quot;&lt;&lt;showReminders tag:\s&quot;&quot; + context.inTiddler.title + \n &quot;\s&quot; format:\s&quot;*DIFF, TITLE\s&quot;$))&quot; ' &gt;&gt;===\n----\n[[Tasks|Task]] by [[Context]]\n&lt;&lt;forEachTiddler\n where 'tiddler.tags.contains(&quot;Context&quot;)'\n sortBy 'tiddler.title'\n write\n '&quot;@@font-size:90%;padding-left:0.5em;[[&quot; + tiddler.title + &quot;]]@@ &quot;+\n/// display a count (by Clint)\n&quot;&lt;&lt;forEachTiddler where \sn&quot; +\n &quot; \s'tiddler.tags.containsAll([\s&quot;Task\s&quot;,\s&quot;&quot;+tiddler.title+&quot;\s&quot;,\s&quot;&quot;+context.inTiddler.title+&quot;\s&quot;]) &amp;&amp; &quot;+\n &quot; !tiddler.tags.contains(\s&quot;Done\s&quot;)\s'\sn&quot; +\n &quot; script \s'function writeTotalTasks(index, count) {if (index == 0) return \s&quot;(\s&quot;+count+\s&quot;)\s&quot;; else return \s&quot;\s&quot;;}\s' &quot;+\n &quot;write \s'writeTotalTasks(index,count)\s'$))&quot; +\n/// end display a count \n&quot;&lt;&lt;newerTiddler name:\s&quot;New Task\s&quot; button:\s&quot;new\s&quot; text:\s&quot;Enter task details\s&quot; tags:\s&quot;Task [[&quot;+tiddler.title+&quot;]] [[&quot;+context.inTiddler.title+&quot;]]\s&quot;$))&quot; +\n (tiddler.tags.contains(&quot;startCollapsed&quot;)?&quot;+++\sn&quot;:&quot;++++&quot;) +\n &quot;&lt;&lt;forEachTiddler where \sn&quot; +\n&quot; \s'tiddler.tags.containsAll([\s&quot;&quot;+context.inTiddler.title+&quot;\s&quot;,\s&quot;Task\s&quot;,\s&quot;&quot;+tiddler.title+&quot;\s&quot;]) &amp;&amp; &quot;+\n &quot; !tiddler.tags.contains(\s&quot;Done\s&quot;)\s'\sn&quot; +\n &quot;$))&quot; +\n &quot;===\sn\sn&quot; +\n &quot;&quot;'\n&gt;&gt;\n\n&lt;/xmp&gt;\n&lt;/td&gt;\n&lt;/tr&gt;&lt;/table&gt;\n&lt;br class=&quot;tagClear&quot;/&gt;\n&lt;!-- &lt;div class=&quot;tagglyTagging&quot; macro=&quot;tagglyListWithSort&quot;&gt;&lt;/div&gt; --&gt;\n\n&lt;!--}}}--&gt;\n\n\n</div>
<div tiddler="QuickOpenTagPlugin" modifier="Ashin" modified="200606020010" created="200601091642" tags="systemConfig">/***\n| Name:|QuickOpenTagPlugin|\n| Purpose:|Makes tag links into a Taggly style open tag plus a normal style drop down menu|\n| Creator:|SimonBaird|\n| Source:|http://simonbaird.com/mptw/#QuickOpenTagPlugin|\n| Requires:|TW 2.x|\n| Version|1.1 (7-Feb-06)|\n\n!History\n* Version 1.1 (07/02/2006)\n** Fix Firefox 1.5.0.1 crashes\n** Updated by ~BidiX[at]~BidiX.info\n* Version 1.0 (?/01/2006)\n** First release\n\n***/\n//{{{\n\n//? ? ? ? &amp;Icirc;?\n\nwindow.createTagButton_orig_mptw = createTagButton;\nwindow.createTagButton = function(place,tag,excludeTiddler) {\n var sp = createTiddlyElement(place,&quot;span&quot;,null,&quot;quickopentag&quot;);\n createTiddlyLink(sp,tag,true,&quot;button&quot;);\n var theTag = createTiddlyButton(sp,config.macros.miniTag.dropdownchar,config.views.wikified.tag.tooltip.format([tag]),onClickTag);\n theTag.setAttribute(&quot;tag&quot;,tag);\n if(excludeTiddler)\n theTag.setAttribute(&quot;tiddler&quot;,excludeTiddler);\n return(theTag);\n};\n\nconfig.macros.miniTag = {handler:function(place,macroName,params,wikifier,paramString,tiddler) {\n var tagged = store.getTaggedTiddlers(tiddler.title);\n if (tagged.length &gt; 0) {\n var theTag = createTiddlyButton(place,config.macros.miniTag.dropdownchar,config.views.wikified.tag.tooltip.format([tiddler.title]),onClickTag);\n theTag.setAttribute(&quot;tag&quot;,tiddler.title);\n theTag.className = &quot;miniTag&quot;;\n }\n}};\n\nconfig.macros.miniTag.dropdownchar = (document.all?&quot;?&quot;:&quot;?&quot;); // the fat one is the only one that works in IE\n\nconfig.macros.allTags.handler = function(place,macroName,params)\n{\n var tags = store.getTags();\n var theDateList = createTiddlyElement(place,&quot;ul&quot;,null,null,null);\n if(tags.length === 0)\n createTiddlyElement(theDateList,&quot;li&quot;,null,&quot;listTitle&quot;,this.noTags);\n for (var t=0; t&lt;tags.length; t++)\n {\n var theListItem =createTiddlyElement(theDateList,&quot;li&quot;,null,null,null);\n var theLink = createTiddlyLink(theListItem,tags[t][0],true);\n var theCount = &quot; (&quot; + tags[t][1] + &quot;)&quot;;\n theLink.appendChild(document.createTextNode(theCount));\n\n var theDropDownBtn = createTiddlyButton(theListItem,&quot; &quot;+config.macros.miniTag.dropdownchar,this.tooltip.format([tags[t][0]]),onClickTag);\n theDropDownBtn.setAttribute(&quot;tag&quot;,tags[t][0]);\n }\n};\n\n\nsetStylesheet(\n &quot;.quickopentag { margin-right:1.2em; border:1px solid #eee; padding:2px; padding-right:0px; padding-left:1px; }\sn&quot;+\n &quot;.quickopentag .tiddlyLink { padding:2px; padding-left:3px; }\sn&quot;+\n &quot;.quickopentag a.button { padding:1px; padding-left:2px; padding-right:2px;}\sn&quot;+\n &quot;a.miniTag {font-size:150%;}\sn&quot;+\n &quot;&quot;,\n&quot;QuickOpenTagStyles&quot;);\n\n//}}}\n\n/***\n&lt;html&gt;&amp;#x22bb; &amp;#x22bd; &amp;#x22c1; &amp;#x25bc; &amp;#x25be;&lt;/html&gt;\n***/\n</div>
<div tiddler="Release Gems-gwt-user on the gems branch" modifier="ashinw" modified="200608051843" created="200608040215" tags="Task [[Gems-GWT-User project roadmap]] Done">* assemble changes\n* prepare branch by copying from 1.0.21 tag\n* overlay on top of copy\n* commit branch</div>
<div tiddler="Reminder" modifier="AshinWima" modified="200606021704" created="200603080710" tags="groupByTag">&lt;&lt;showReminders&gt;&gt;</div>
<div tiddler="ReminderMacros" modifier="SimonBaird" modified="200603091331" created="200603080706" tags="systemConfig">/***\n|''Name:''|ReminderPlugin|\n|''Version:''|2.3.8 (Mar 9, 2006)|\n|''Source:''|http://www.geocities.com/allredfaq/reminderMacros.html|\n|''Author:''|Jeremy Sheeley(pop1280 [at] excite [dot] com)|\n|''Licence:''|[[BSD open source license]]|\n|''Macros:''|reminder, showreminders, displayTiddlersWithReminders, newReminder|\n|''TiddlyWiki:''|2.0+|\n|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|\n\n!Description\nThis plugin provides macros for tagging a date with a reminder. Use the {{{reminder}}} macro to do this. The {{{showReminders}}} and {{{displayTiddlersWithReminder}}} macros automatically search through all available tiddlers looking for upcoming reminders.\n\n!Installation\n* Create a new tiddler in your tiddlywiki titled ReminderPlugin and give it the {{{systemConfig}}} tag. The tag is important because it tells TW that this is executable code.\n* Double click this tiddler, and copy all the text from the tiddler's body.\n* Paste the text into the body of the new tiddler in your TW.\n* Save and reload your TW.\n* You can copy some examples into your TW as well. See [[Simple examples]], [[Holidays]], [[showReminders]] and [[Personal Reminders]]\n\n!Syntax:\n|&gt;|See [[ReminderSyntax]] and [[showRemindersSyntax]]|\n\n!Revision history\n* v2.3.8 (Mar 9, 2006)\n**Bug fix: A global variable had snuck in, which was killing FF 1.5.0.1\n**Feature: You can now use TIDDLER and TIDDLERNAME in a regular reminder format\n* v2.3.6 (Mar 1, 2006)\n**Bug fix: Reminders for today weren't being matched sometimes.\n**Feature: Solidified integration with DatePlugin and CalendarPlugin\n**Feature: Recurring reminders will now return multiple hits in showReminders and the calendar.\n**Feature: Added TIDDLERNAME to the replacements for showReminders format, for plugins that need the title without brackets.\n* v2.3.5 (Feb 8, 2006)\n**Bug fix: Sped up reminders lots. Added a caching mechanism for reminders that have already been matched.\n* v2.3.4 (Feb 7, 2006)\n**Bug fix: Cleaned up code to hopefully prevent the Firefox 1.5.0.1 crash that was causing lots of plugins \nto crash Firefox. Thanks to http://www.jslint.com\n* v2.3.3 (Feb 2, 2006)\n**Feature: newReminder now has drop down lists instead of text boxes.\n**Bug fix: A trailing space in a title would trigger an infinite loop.\n**Bug fix: using tag:&quot;birthday !reminder&quot; would filter differently than tag:&quot;!reminder birthday&quot;\n* v2.3.2 (Jan 21, 2006)\n**Feature: newReminder macro, which will let you easily add a reminder to a tiddler. Thanks to Eric Shulman (http://www.elsdesign.com) for the code to do this.\n** Bug fix: offsetday was not working sometimes\n** Bug fix: when upgrading to 2.0, I included a bit to exclude tiddlers tagged with excludeSearch. I've reverted back to searching through all tiddlers\n* v2.3.1 (Jan 7, 2006)\n**Feature: 2.0 compatibility\n**Feature AlanH sent some code to make sure that showReminders prints a message if no reminders are found.\n* v2.3.0 (Jan 3, 2006)\n** Bug Fix: Using &quot;Last Sunday (-0)&quot; as a offsetdayofweek wasn't working.\n** Bug Fix: Daylight Savings time broke offset based reminders (for example year:2005 month:8 day:23 recurdays:7 would match Monday instead of Tuesday during DST.\n\n!Code\n***/\n//{{{\n\n//============================================================================\n//============================================================================\n// ReminderPlugin\n//============================================================================\n//============================================================================\n\nversion.extensions.ReminderPlugin = {major: 2, minor: 3, revision: 8, date: new Date(2006,3,9), source: &quot;http://www.geocities.com/allredfaq/reminderMacros.html&quot;};\n\n//============================================================================\n// Configuration\n// Modify this section to change the defaults for \n// leadtime and display strings\n//============================================================================\n\nconfig.macros.reminders = {};\nconfig.macros[&quot;reminder&quot;] = {};\nconfig.macros[&quot;newReminder&quot;] = {};\nconfig.macros[&quot;showReminders&quot;] = {};\nconfig.macros[&quot;displayTiddlersWithReminders&quot;] = {};\n\nconfig.macros.reminders[&quot;defaultLeadTime&quot;] = [0,6000];\nconfig.macros.reminders[&quot;defaultReminderMessage&quot;] = &quot;DIFF: TITLE on DATE ANNIVERSARY&quot;;\nconfig.macros.reminders[&quot;defaultShowReminderMessage&quot;] = &quot;DIFF: TITLE on DATE ANNIVERSARY -- TIDDLER&quot;;\nconfig.macros.reminders[&quot;defaultAnniversaryMessage&quot;] = &quot;(DIFF)&quot;;\nconfig.macros.reminders[&quot;untitledReminder&quot;] = &quot;Untitled Reminder&quot;;\nconfig.macros.reminders[&quot;noReminderFound&quot;] = &quot;Couldn't find a match for TITLE in the next LEADTIMEUPPER days.&quot;\nconfig.macros.reminders[&quot;todayString&quot;] = &quot;Today&quot;;\nconfig.macros.reminders[&quot;tomorrowString&quot;] = &quot;Tomorrow&quot;;\nconfig.macros.reminders[&quot;ndaysString&quot;] = &quot;DIFF days&quot;;\nconfig.macros.reminders[&quot;emtpyShowRemindersString&quot;] = &quot;There are no upcoming events&quot;;\n\n\n//============================================================================\n// Code\n// You should not need to edit anything \n// below this. Make sure to edit this tiddler and copy \n// the code from the text box, to make sure that \n// tiddler rendering doesn't interfere with the copy \n// and paste.\n//============================================================================\n\n// This line is to preserve 1.2 compatibility\n if (!story) var story=window; \n//this object will hold the cache of reminders, so that we don't\n//recompute the same reminder over again.\nvar reminderCache = {};\n\nconfig.macros.showReminders.handler = function showReminders(place,macroName,params)\n{\n var now = new Date().getMidnight();\n var paramHash = {};\n var leadtime = [0,14];\n paramHash = getParamsForReminder(params);\n var bProvidedDate = (paramHash[&quot;year&quot;] != null) || \n (paramHash[&quot;month&quot;] != null) || \n (paramHash[&quot;day&quot;] != null) || \n (paramHash[&quot;dayofweek&quot;] != null);\n if (paramHash[&quot;leadtime&quot;] != null)\n {\n leadtime = paramHash[&quot;leadtime&quot;];\n if (bProvidedDate)\n {\n //If they've entered a day, we need to make \n //sure to find it. We'll reset the \n //leadtime a few lines down.\n paramHash[&quot;leadtime&quot;] = [-10000, 10000];\n }\n }\n var matchedDate = now;\n if (bProvidedDate)\n {\n var leadTimeLowerBound = new Date().getMidnight().addDays(paramHash[&quot;leadtime&quot;][0]);\n var leadTimeUpperBound = new Date().getMidnight().addDays(paramHash[&quot;leadtime&quot;][1]);\n matchedDate = findDateForReminder(paramHash, new Date().getMidnight(), leadTimeLowerBound, leadTimeUpperBound); \n }\n\n var arr = findTiddlersWithReminders(matchedDate, leadtime, paramHash[&quot;tag&quot;], paramHash[&quot;limit&quot;]);\n var elem = createTiddlyElement(place,&quot;span&quot;,null,null, null);\n var mess = &quot;&quot;;\n if (arr.length == 0)\n {\n mess += config.macros.reminders.emtpyShowRemindersString; \n }\n for (var j = 0; j &lt; arr.length; j++)\n {\n if (paramHash[&quot;format&quot;] != null)\n {\n arr[j][&quot;params&quot;][&quot;format&quot;] = paramHash[&quot;format&quot;];\n }\n else\n {\n arr[j][&quot;params&quot;][&quot;format&quot;] = config.macros.reminders[&quot;defaultShowReminderMessage&quot;];\n }\n mess += getReminderMessageForDisplay(arr[j][&quot;diff&quot;], arr[j][&quot;params&quot;], arr[j][&quot;matchedDate&quot;], arr[j][&quot;tiddler&quot;]);\n mess += &quot;\sn&quot;;\n }\n wikify(mess, elem, null, null);\n};\n\n\nconfig.macros.displayTiddlersWithReminders.handler = function displayTiddlersWithReminders(place,macroName,params)\n{\n var now = new Date().getMidnight();\n var paramHash = {};\n var leadtime = [0,14];\n paramHash = getParamsForReminder(params);\n var bProvidedDate = (paramHash[&quot;year&quot;] != null) || \n (paramHash[&quot;month&quot;] != null) || \n (paramHash[&quot;day&quot;] != null) || \n (paramHash[&quot;dayofweek&quot;] != null);\n if (paramHash[&quot;leadtime&quot;] != null)\n {\n leadtime = paramHash[&quot;leadtime&quot;];\n if (bProvidedDate)\n {\n //If they've entered a day, we need to make \n //sure to find it. We'll reset the leadtime \n //a few lines down.\n paramHash[&quot;leadtime&quot;] = [-10000,10000];\n }\n }\n var matchedDate = now;\n if (bProvidedDate)\n {\n var leadTimeLowerBound = new Date().getMidnight().addDays(paramHash[&quot;leadtime&quot;][0]);\n var leadTimeUpperBound = new Date().getMidnight().addDays(paramHash[&quot;leadtime&quot;][1]);\n matchedDate = findDateForReminder(paramHash, new Date().getMidnight(), leadTimeLowerBound, leadTimeUpperBound); \n }\n var arr = findTiddlersWithReminders(matchedDate, leadtime, paramHash[&quot;tag&quot;], paramHash[&quot;limit&quot;]);\n for (var j = 0; j &lt; arr.length; j++)\n {\n displayTiddler(null, arr[j][&quot;tiddler&quot;], 0, null, false, false, false);\n }\n};\n\nconfig.macros.reminder.handler = function reminder(place,macroName,params)\n{\n var dateHash = getParamsForReminder(params);\n if (dateHash[&quot;hidden&quot;] != null)\n {\n return;\n }\n var leadTime = dateHash[&quot;leadtime&quot;];\n if (leadTime == null)\n {\n leadTime = config.macros.reminders[&quot;defaultLeadTime&quot;]; \n }\n var leadTimeLowerBound = new Date().getMidnight().addDays(leadTime[0]);\n var leadTimeUpperBound = new Date().getMidnight().addDays(leadTime[1]);\n var matchedDate = findDateForReminder(dateHash, new Date().getMidnight(), leadTimeLowerBound, leadTimeUpperBound);\n if (!window.story) \n {\n window.story=window; \n }\n if (!store.getTiddler) \n {\n store.getTiddler=function(title) {return this.tiddlers[title];};\n }\n var title = window.story.findContainingTiddler(place).id.substr(7);\n if (matchedDate != null)\n {\n var diff = matchedDate.getDifferenceInDays(new Date().getMidnight());\n var elem = createTiddlyElement(place,&quot;span&quot;,null,null, null);\n var mess = getReminderMessageForDisplay(diff, dateHash, matchedDate, title);\n wikify(mess, elem, null, null);\n }\n else\n {\n createTiddlyElement(place,&quot;span&quot;,null,null, config.macros.reminders[&quot;noReminderFound&quot;].replace(&quot;TITLE&quot;, dateHash[&quot;title&quot;]).replace(&quot;LEADTIMEUPPER&quot;, leadTime[1]).replace(&quot;LEADTIMELOWER&quot;, leadTime[0]).replace(&quot;TIDDLERNAME&quot;, title).replace(&quot;TIDDLER&quot;, &quot;[[&quot; + title + &quot;]]&quot;) );\n }\n};\n\nconfig.macros.newReminder.handler = function newReminder(place,macroName,params)\n{\n var today=new Date().getMidnight();\n var formstring = '&lt;html&gt;&lt;form&gt;Year: &lt;select name=&quot;year&quot;&gt;&lt;option value=&quot;&quot;&gt;Every year&lt;/option&gt;';\n for (var i = 0; i &lt; 5; i++)\n {\n formstring += '&lt;option' + ((i == 0) ? ' selected' : '') + ' value=&quot;' + (today.getFullYear() +i) + '&quot;&gt;' + (today.getFullYear() + i) + '&lt;/option&gt;';\n }\n formstring += '&lt;/select&gt;&amp;nbsp;&amp;nbsp;Month:&lt;select name=&quot;month&quot;&gt;&lt;option value=&quot;&quot;&gt;Every month&lt;/option&gt;';\n for (i = 0; i &lt; 12; i++)\n {\n formstring += '&lt;option' + ((i == today.getMonth()) ? ' selected' : '') + ' value=&quot;' + (i+1) + '&quot;&gt;' + config.messages.dates.months[i] + '&lt;/option&gt;';\n }\n formstring += '&lt;/select&gt;&amp;nbsp;&amp;nbsp;Day:&lt;select name=&quot;day&quot;&gt;&lt;option value=&quot;&quot;&gt;Every day&lt;/option&gt;';\n for (i = 1; i &lt; 32; i++)\n {\n formstring += '&lt;option' + ((i == (today.getDate() )) ? ' selected' : '') + ' value=&quot;' + i + '&quot;&gt;' + i + '&lt;/option&gt;';\n }\n\nformstring += '&lt;/select&gt;&amp;nbsp;&amp;nbsp;Reminder Title:&lt;input type=&quot;text&quot; size=&quot;40&quot; name=&quot;title&quot; value=&quot;please enter a title&quot; onfocus=&quot;this.select();&quot;&gt;&lt;input type=&quot;button&quot; value=&quot;ok&quot; onclick=&quot;addReminderToTiddler(this.form)&quot;&gt;&lt;/form&gt;&lt;/html&gt;';\n\n var panel = config.macros.slider.createSlider(place,null,&quot;New Reminder&quot;,&quot;Open a form to add a new reminder to this tiddler&quot;);\n wikify(formstring ,panel,null,store.getTiddler(params[1]));\n};\n\n// onclick: process input and insert reminder at 'marker'\nwindow.addReminderToTiddler = function(form) {\n if (!window.story) \n {\n window.story=window; \n }\n if (!store.getTiddler) \n {\n store.getTiddler=function(title) {return this.tiddlers[title];};\n }\n var title = window.story.findContainingTiddler(form).id.substr(7);\n var tiddler=store.getTiddler(title);\n var txt='\sn&lt;&lt;reminder ';\n if (form.year.value != &quot;&quot;)\n txt += 'year:'+form.year.value + ' ';\n if (form.month.value != &quot;&quot;)\n txt += 'month:'+form.month.value + ' ';\n if (form.day.value != &quot;&quot;)\n txt += 'day:'+form.day.value + ' ';\n txt += 'title:&quot;'+form.title.value+'&quot; ';\n txt +='&gt;&gt;';\n tiddler.set(null,tiddler.text + txt);\n window.story.refreshTiddler(title,1,true);\n store.setDirty(true);\n};\n\nfunction hasTag(tiddlerTags, tagFilters)\n{\n //Make sure we respond well to empty tiddlerTaglists or tagFilterlists\n if (tagFilters.length==0 || tiddlerTags.length==0)\n {\n return true;\n }\n\n var bHasTag = false;\n \n /*bNoPos says: &quot;'till now there has been no check using a positive filter&quot;\n Imagine a filterlist consisting of 1 negative filter:\n If the filter isn't matched, we want hasTag to be true.\n Yet bHasTag is still false ('cause only positive filters cause bHasTag to change)\n \n If no positive filters are present bNoPos is true, and no negative filters are matched so we have not returned false\n Thus: hasTag returns true.\n \n If at any time a positive filter is encountered, we want at least one of the tags to match it, so we turn bNoPos to false, which\n means bHasTag must be true for hasTag to return true*/\n var bNoPos=true;\n \nfor (var t3 = 0; t3 &lt; tagFilters.length; t3++)\n {\n for(var t2=0; t2&lt;tiddlerTags.length; t2++)\n {\n if (tagFilters[t3].length &gt; 1 &amp;&amp; tagFilters[t3].charAt(0) == '!') \n {\n if (tiddlerTags[t2] == tagFilters[t3].substring(1))\n {\n //If at any time a negative filter is matched, we return false\n return false;\n }\n }\n else \n {\n if (bNoPos)\n {\n //We encountered the first positive filter\n bNoPos=false;\n }\n if (tiddlerTags[t2] == tagFilters[t3])\n {\n //A positive filter is matched. As long as no negative filter is matched, hasTag will return true\n bHasTag=true;\n }\n }\n }\n }\n return (bNoPos || bHasTag);\n};\n\n//This function searches all tiddlers for the reminder //macro. It is intended that other plugins (like //calendar) will use this function to query for \n//upcoming reminders.\n//The arguments to this function filter out reminders //based on when they will fire.\n//\n//ARGUMENTS:\n//baseDate is the date that is used as &quot;now&quot;. \n//leadtime is a two element int array, with leadtime[0] \n// as the lower bound and leadtime[1] as the\n// upper bound. A reasonable default is [0,14]\n//tags is a space-separated list of tags to use to filter \n// tiddlers. If a tag name begins with an !, then \n// only tiddlers which do not have that tag will \n// be considered. For example &quot;examples holidays&quot; \n// will search for reminders in any tiddlers that \n// are tagged with examples or holidays and \n// &quot;!examples !holidays&quot; will search for reminders \n// in any tiddlers that are not tagged with \n// examples or holidays. Pass in null to search \n// all tiddlers.\n//limit. If limit is null, individual reminders can \n// override the leadtime specified earlier. \n// Pass in 1 in order to override that behavior.\n\nwindow.findTiddlersWithReminders = function findTiddlersWithReminders(baseDate, leadtime, tags, limit)\n{\n//function(searchRegExp,sortField,excludeTag)\n// var macroPattern = &quot;&lt;&lt;([^&gt;\s\s]+)(?:\s\s*)([^&gt;]*)&gt;&gt;&quot;;\n var macroPattern = &quot;&lt;&lt;(reminder)(.*)&gt;&gt;&quot;;\n var macroRegExp = new RegExp(macroPattern,&quot;mg&quot;);\n var matches = store.search(macroRegExp,&quot;title&quot;,&quot;&quot;);\n var arr = [];\n var tagsArray = null;\n if (tags != null)\n {\n tagsArray = tags.split(&quot; &quot;);\n }\n for(var t=matches.length-1; t&gt;=0; t--)\n {\n if (tagsArray != null)\n {\n //If they specified tags to filter on, and this tiddler doesn't \n //match, skip it entirely.\n if ( ! hasTag(matches[t].tags, tagsArray))\n {\n continue;\n }\n }\n\n var targetText = matches[t].text;\n do {\n // Get the next formatting match\n var formatMatch = macroRegExp.exec(targetText);\n if(formatMatch &amp;&amp; formatMatch[1] != null &amp;&amp; formatMatch[1].toLowerCase() == &quot;reminder&quot;)\n {\n //Find the matching date.\n \n var params = formatMatch[2] != null ? formatMatch[2].readMacroParams() : {};\n var dateHash = getParamsForReminder(params);\n if (limit != null || dateHash[&quot;leadtime&quot;] == null)\n {\n if (leadtime == null)\n dateHash[&quot;leadtime&quot;] = leadtime;\n else\n {\n dateHash[&quot;leadtime&quot;] = [];\n dateHash[&quot;leadtime&quot;][0] = leadtime[0];\n dateHash[&quot;leadtime&quot;][1] = leadtime[1];\n }\n }\n if (dateHash[&quot;leadtime&quot;] == null)\n dateHash[&quot;leadtime&quot;] = config.macros.reminders[&quot;defaultLeadTime&quot;]; \n var leadTimeLowerBound = baseDate.addDays(dateHash[&quot;leadtime&quot;][0]);\n var leadTimeUpperBound = baseDate.addDays(dateHash[&quot;leadtime&quot;][1]);\n var matchedDate = findDateForReminder(dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound);\n while (matchedDate != null)\n {\n var hash = {};\n hash[&quot;diff&quot;] = matchedDate.getDifferenceInDays(baseDate);\n hash[&quot;matchedDate&quot;] = new Date(matchedDate.getFullYear(), matchedDate.getMonth(), matchedDate.getDate(), 0, 0);\n hash[&quot;params&quot;] = cloneParams(dateHash);\n hash[&quot;tiddler&quot;] = matches[t].title;\n hash[&quot;tags&quot;] = matches[t].tags;\n arr.pushUnique(hash);\n if (dateHash[&quot;recurdays&quot;] != null || (dateHash[&quot;year&quot;] == null))\n {\n leadTimeLowerBound = leadTimeLowerBound.addDays(matchedDate.getDifferenceInDays(leadTimeLowerBound)+ 1);\n matchedDate = findDateForReminder(dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound);\n }\n else matchedDate = null;\n }\n }\n }while(formatMatch);\n }\n if(arr.length &gt; 1) //Sort the array by number of days remaining.\n {\n arr.sort(function (a,b) {if(a[&quot;diff&quot;] == b[&quot;diff&quot;]) {return(0);} else {return (a[&quot;diff&quot;] &lt; b[&quot;diff&quot;]) ? -1 : +1; } });\n }\n return arr;\n};\n\n//This function takes the reminder macro parameters and\n//generates the string that is used for display.\n//This function is not intended to be called by \n//other plugins.\n window.getReminderMessageForDisplay= function getReminderMessageForDisplay(diff, params, matchedDate, tiddlerTitle)\n{\n var anniversaryString = &quot;&quot;;\n var reminderTitle = params[&quot;title&quot;];\n if (reminderTitle == null)\n {\n reminderTitle = config.macros.reminders[&quot;untitledReminder&quot;];\n }\n if (params[&quot;firstyear&quot;] != null)\n {\n anniversaryString = config.macros.reminders[&quot;defaultAnniversaryMessage&quot;].replace(&quot;DIFF&quot;, (matchedDate.getFullYear() - params[&quot;firstyear&quot;]));\n }\n var mess = &quot;&quot;;\n var diffString = &quot;&quot;;\n if (diff == 0)\n {\n diffString = config.macros.reminders[&quot;todayString&quot;];\n }\n else if (diff == 1)\n {\n diffString = config.macros.reminders[&quot;tomorrowString&quot;];\n }\n else\n {\n diffString = config.macros.reminders[&quot;ndaysString&quot;].replace(&quot;DIFF&quot;, diff);\n }\n var format = config.macros.reminders[&quot;defaultReminderMessage&quot;];\n if (params[&quot;format&quot;] != null)\n {\n format = params[&quot;format&quot;];\n }\n mess = format;\n//HACK! -- Avoid replacing DD in TIDDLER with the date\n mess = mess.replace(/TIDDLER/g, &quot;TIDELER&quot;);\n mess = matchedDate.formatStringDateOnly(mess);\n mess = mess.replace(/TIDELER/g, &quot;TIDDLER&quot;);\n if (tiddlerTitle != null)\n {\n mess = mess.replace(/TIDDLERNAME/g, tiddlerTitle);\n mess = mess.replace(/TIDDLER/g, &quot;[[&quot; + tiddlerTitle + &quot;]]&quot;);\n }\n \n mess = mess.replace(&quot;DIFF&quot;, diffString).replace(&quot;TITLE&quot;, reminderTitle).replace(&quot;DATE&quot;, matchedDate.formatString(&quot;DDD MMM DD, YYYY&quot;)).replace(&quot;ANNIVERSARY&quot;, anniversaryString);\n return mess;\n};\n\n// Parse out the macro parameters into a hashtable. This\n// handles the arguments for reminder, showReminders and \n// displayTiddlersWithReminders.\nwindow.getParamsForReminder = function getParamsForReminder(params)\n{\n var dateHash = {};\n var type = &quot;&quot;;\n var num = 0;\n var title = &quot;&quot;;\n for(var t=0; t&lt;params.length; t++)\n {\n var split = params[t].split(&quot;:&quot;);\n type = split[0].toLowerCase();\n var value = split[1];\n for (var i=2; i &lt; split.length; i++)\n {\n value += &quot;:&quot; + split[i];\n }\n if (type == &quot;nolinks&quot; || type == &quot;limit&quot; || type == &quot;hidden&quot;)\n {\n num = 1;\n }\n else if (type == &quot;leadtime&quot;)\n {\n var leads = value.split(&quot;...&quot;);\n if (leads.length == 1)\n {\n leads[1]= leads[0];\n leads[0] = 0;\n }\n leads[0] = parseInt(leads[0], 10);\n leads[1] = parseInt(leads[1], 10);\n num = leads;\n }\n else if (type == &quot;offsetdayofweek&quot;)\n {\n if (value.substr(0,1) == &quot;-&quot;)\n {\n dateHash[&quot;negativeOffsetDayOfWeek&quot;] = 1;\n value = value.substr(1);\n }\n num = parseInt(value, 10);\n }\n else if (type != &quot;title&quot; &amp;&amp; type != &quot;tag&quot; &amp;&amp; type != &quot;format&quot;)\n {\n num = parseInt(value, 10);\n }\n else\n {\n title = value;\n t++;\n while (title.substr(0,1) == '&quot;' &amp;&amp; title.substr(title.length - 1,1) != '&quot;' &amp;&amp; params[t] != undefined)\n {\n title += &quot; &quot; + params[t++];\n }\n //Trim off the leading and trailing quotes\n if (title.substr(0,1) == &quot;\s&quot;&quot; &amp;&amp; title.substr(title.length - 1,1)== &quot;\s&quot;&quot;)\n {\n title = title.substr(1, title.length - 2);\n t--;\n }\n num = title;\n }\n dateHash[type] = num;\n }\n //date is synonymous with day\n if (dateHash[&quot;day&quot;] == null)\n {\n dateHash[&quot;day&quot;] = dateHash[&quot;date&quot;];\n }\n return dateHash;\n};\n\n//This function finds the date specified in the reminder \n//parameters. It will return null if no match can be\n//found. This function is not intended to be used by\n//other plugins.\nwindow.findDateForReminder= function findDateForReminder( dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound)\n{\n if (baseDate == null)\n {\n baseDate = new Date().getMidnight();\n }\n var hashKey = baseDate.convertToYYYYMMDDHHMM();\n for (var k in dateHash)\n {\n hashKey += &quot;,&quot; + k + &quot;|&quot; + dateHash[k];\n }\n hashKey += &quot;,&quot; + leadTimeLowerBound.convertToYYYYMMDDHHMM();\n hashKey += &quot;,&quot; + leadTimeUpperBound.convertToYYYYMMDDHHMM();\n if (reminderCache[hashKey] == null)\n {\n //If we don't find a match in this run, then we will\n //cache that the reminder can't be matched.\n reminderCache[hashKey] = false;\n }\n else if (reminderCache[hashKey] == false)\n {\n //We've already tried this date and failed\n return null;\n }\n else\n {\n return reminderCache[hashKey];\n }\n \n var bOffsetSpecified = dateHash[&quot;offsetyear&quot;] != null || \n dateHash[&quot;offsetmonth&quot;] != null || \n dateHash[&quot;offsetday&quot;] != null || \n dateHash[&quot;offsetdayofweek&quot;] != null || \n dateHash[&quot;recurdays&quot;] != null;\n \n // If we are matching the base date for a dayofweek offset, look for the base date a \n //little further back.\n var tmp1leadTimeLowerBound = leadTimeLowerBound; \n if ( dateHash[&quot;offsetdayofweek&quot;] != null)\n {\n tmp1leadTimeLowerBound = leadTimeLowerBound.addDays(-6); \n }\n var matchedDate = baseDate.findMatch(dateHash, tmp1leadTimeLowerBound, leadTimeUpperBound);\n if (matchedDate != null)\n {\n var newMatchedDate = matchedDate;\n if (dateHash[&quot;recurdays&quot;] != null)\n {\n while (newMatchedDate.getTime() &lt; leadTimeLowerBound.getTime())\n {\n newMatchedDate = newMatchedDate.addDays(dateHash[&quot;recurdays&quot;]);\n }\n }\n else if (dateHash[&quot;offsetyear&quot;] != null || \n dateHash[&quot;offsetmonth&quot;] != null || \n dateHash[&quot;offsetday&quot;] != null || \n dateHash[&quot;offsetdayofweek&quot;] != null)\n {\n var tmpdateHash = cloneParams(dateHash);\n tmpdateHash[&quot;year&quot;] = dateHash[&quot;offsetyear&quot;];\n tmpdateHash[&quot;month&quot;] = dateHash[&quot;offsetmonth&quot;];\n tmpdateHash[&quot;day&quot;] = dateHash[&quot;offsetday&quot;];\n tmpdateHash[&quot;dayofweek&quot;] = dateHash[&quot;offsetdayofweek&quot;];\n var tmpleadTimeLowerBound = leadTimeLowerBound;\n var tmpleadTimeUpperBound = leadTimeUpperBound;\n if (tmpdateHash[&quot;offsetdayofweek&quot;] != null)\n {\n if (tmpdateHash[&quot;negativeOffsetDayOfWeek&quot;] == 1)\n {\n tmpleadTimeLowerBound = matchedDate.addDays(-6);\n tmpleadTimeUpperBound = matchedDate;\n\n }\n else\n {\n tmpleadTimeLowerBound = matchedDate;\n tmpleadTimeUpperBound = matchedDate.addDays(6);\n }\n\n }\n newMatchedDate = matchedDate.findMatch(tmpdateHash, tmpleadTimeLowerBound, tmpleadTimeUpperBound);\n //The offset couldn't be matched. return null.\n if (newMatchedDate == null)\n {\n return null;\n }\n }\n if (newMatchedDate.isBetween(leadTimeLowerBound, leadTimeUpperBound))\n {\n reminderCache[hashKey] = newMatchedDate;\n return newMatchedDate;\n }\n }\n return null;\n};\n\n//This does much the same job as findDateForReminder, but\n//this one doesn't deal with offsets or recurring \n//reminders.\nDate.prototype.findMatch = function findMatch(dateHash, leadTimeLowerBound, leadTimeUpperBound)\n{\n\n var bSpecifiedYear = (dateHash[&quot;year&quot;] != null);\n var bSpecifiedMonth = (dateHash[&quot;month&quot;] != null);\n var bSpecifiedDay = (dateHash[&quot;day&quot;] != null);\n var bSpecifiedDayOfWeek = (dateHash[&quot;dayofweek&quot;] != null);\n if (bSpecifiedYear &amp;&amp; bSpecifiedMonth &amp;&amp; bSpecifiedDay)\n {\n return new Date(dateHash[&quot;year&quot;], dateHash[&quot;month&quot;]-1, dateHash[&quot;day&quot;], 0, 0);\n }\n var bMatchedYear = !bSpecifiedYear;\n var bMatchedMonth = !bSpecifiedMonth;\n var bMatchedDay = !bSpecifiedDay;\n var bMatchedDayOfWeek = !bSpecifiedDayOfWeek;\n if (bSpecifiedDay &amp;&amp; bSpecifiedMonth &amp;&amp; !bSpecifiedYear &amp;&amp; !bSpecifiedDayOfWeek)\n {\n\n //Shortcut -- First try this year. If it's too small, try next year.\n var tmpMidnight = this.getMidnight();\n var tmpDate = new Date(this.getFullYear(), dateHash[&quot;month&quot;]-1, dateHash[&quot;day&quot;], 0,0);\n if (tmpDate.getTime() &lt; leadTimeLowerBound.getTime())\n {\n tmpDate = new Date((this.getFullYear() + 1), dateHash[&quot;month&quot;]-1, dateHash[&quot;day&quot;], 0,0);\n }\n if ( tmpDate.isBetween(leadTimeLowerBound, leadTimeUpperBound))\n {\n return tmpDate;\n }\n else\n {\n return null;\n }\n }\n\n var newDate = leadTimeLowerBound; \n while (newDate.isBetween(leadTimeLowerBound, leadTimeUpperBound))\n {\n var tmp = testDate(newDate, dateHash, bSpecifiedYear, bSpecifiedMonth, bSpecifiedDay, bSpecifiedDayOfWeek);\n if (tmp != null)\n return tmp;\n newDate = newDate.addDays(1);\n }\n};\n\nfunction testDate(testMe, dateHash, bSpecifiedYear, bSpecifiedMonth, bSpecifiedDay, bSpecifiedDayOfWeek)\n{\n var bMatchedYear = !bSpecifiedYear;\n var bMatchedMonth = !bSpecifiedMonth;\n var bMatchedDay = !bSpecifiedDay;\n var bMatchedDayOfWeek = !bSpecifiedDayOfWeek;\n if (bSpecifiedYear)\n {\n bMatchedYear = (dateHash[&quot;year&quot;] == testMe.getFullYear());\n }\n if (bSpecifiedMonth)\n {\n bMatchedMonth = ((dateHash[&quot;month&quot;] - 1) == testMe.getMonth() );\n }\n if (bSpecifiedDay)\n {\n bMatchedDay = (dateHash[&quot;day&quot;] == testMe.getDate());\n }\n if (bSpecifiedDayOfWeek)\n {\n bMatchedDayOfWeek = (dateHash[&quot;dayofweek&quot;] == testMe.getDay());\n }\n\n if (bMatchedYear &amp;&amp; bMatchedMonth &amp;&amp; bMatchedDay &amp;&amp; bMatchedDayOfWeek)\n {\n return testMe;\n }\n};\n\n//Returns true if the date is in between two given dates\nDate.prototype.isBetween = function isBetween(lowerBound, upperBound)\n{\n return (this.getTime() &gt;= lowerBound.getTime() &amp;&amp; this.getTime() &lt;= upperBound.getTime());\n}\n//Return a new date, with the time set to midnight (0000)\nDate.prototype.getMidnight = function getMidnight()\n{\n return new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0);\n};\n// Add the specified number of days to a date.\nDate.prototype.addDays = function addDays(numberOfDays)\n{\n return new Date(this.getFullYear(), this.getMonth(), this.getDate() + numberOfDays, 0, 0);\n};\n//Return the number of days between two dates.\nDate.prototype.getDifferenceInDays = function getDifferenceInDays(otherDate)\n{\n//I have to do it this way, because this way ignores daylight savings\n var tmpDate = this.addDays(0);\n if (this.getTime() &gt; otherDate.getTime())\n {\n var i = 0;\n for (i = 0; tmpDate.getTime() &gt; otherDate.getTime(); i++)\n {\n tmpDate = tmpDate.addDays(-1);\n }\n return i;\n }\n else\n {\n var i = 0;\n for (i = 0; tmpDate.getTime() &lt; otherDate.getTime(); i++)\n {\n tmpDate = tmpDate.addDays(1);\n }\n return i * -1;\n }\n return 0;\n};\nfunction cloneParams(what) {\n var tmp = {};\n for (var i in what) {\n tmp[i] = what[i];\n }\n return tmp;\n}\n// Substitute date components into a string\nDate.prototype.formatStringDateOnly = function formatStringDateOnly(template)\n{\n template = template.replace(&quot;YYYY&quot;,this.getFullYear());\n template = template.replace(&quot;YY&quot;,String.zeroPad(this.getFullYear()-2000,2));\n template = template.replace(&quot;MMM&quot;,config.messages.dates.months[this.getMonth()]);\n template = template.replace(&quot;0MM&quot;,String.zeroPad(this.getMonth()+1,2));\n template = template.replace(&quot;MM&quot;,this.getMonth()+1);\n template = template.replace(&quot;DDD&quot;,config.messages.dates.days[this.getDay()]);\n template = template.replace(&quot;0DD&quot;,String.zeroPad(this.getDate(),2));\n template = template.replace(&quot;DD&quot;,this.getDate());\n return template;\n};\n\n//}}}</div>
<div tiddler="RenameTagsPlugin" modifier="SimonBaird" modified="200603052259" created="200603051724" tags="systemConfig">/***\n| Name:|RenameTagsPlugin|\n| Purpose:|Allows you to easily rename tags|\n| Creator:|SimonBaird|\n| Source:|http://simonbaird.com/mptw/#RenameTagsPlugin|\n| Version:|1.0.1 (5-Mar-06)|\n\n!Description\nIf you rename a tiddler/tag that is tagging other tiddlers this plugin will ask you if you want to rename the tag in each tiddler where it is used. This is essential if you use tags and ever want to rename them. To use it, open the tag you want to rename as a tiddler (it's the last option in the tag popup menu), edit it, rename it and click done. You will asked if you want to rename the tag. Click OK to rename the tag in the tiddlers that use it. Click Cancel to not rename the tag.\n\n!Example\nTry renaming [[Plugins]] or [[CSS]] on this site.\n\n!History\n* 1.0.1 (5-Mar-06) - Added feature to allow renaming of tags without side-effect of creating a tiddler\n* 1.0.0 (5-Mar-06) - First working version\n\n!Code\n***/\n//{{{\n\nversion.extensions.RenameTagsPlugin = {\n major: 1, minor: 0, revision: 0,\n date: new Date(2006,3,5),\n source: &quot;http://simonbaird.com/mptw/#RenameTagsPlugin&quot;\n};\n\nconfig.macros.RenameTagsPlugin = {};\nconfig.macros.RenameTagsPlugin.prompt = &quot;Rename the tag '%0' to '%1' in %2 tidder%3?&quot;;\n\n// these are very useful, perhaps they should be in the core\nif (!store.addTag) {\n store.addTag = function(title,tag) {\n var t=this.getTiddler(title); if (!t || !t.tags) return;\n t.tags.push(tag);\n };\n};\n\nif (!store.removeTag) {\n store.removeTag = function(title,tag) {\n var t=this.getTiddler(title); if (!t || !t.tags) return;\n if (t.tags.find(tag)!=null) t.tags.splice(t.tags.find(tag),1);\n };\n};\n\nstore.saveTiddler_orig_tagrename = store.saveTiddler;\nstore.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags) {\n if (title != newTitle &amp;&amp; this.getTaggedTiddlers(title).length &gt; 0) {\n // then we are renaming a tag\n var tagged = this.getTaggedTiddlers(title);\n if (confirm(config.macros.RenameTagsPlugin.prompt.format([title,newTitle,tagged.length,tagged.length&gt;1?&quot;s&quot;:&quot;&quot;]))) {\n for (var i=0;i&lt;tagged.length;i++) {\n store.removeTag(tagged[i].title,title);\n store.addTag(tagged[i].title,newTitle);\n // if tiddler is visible refresh it to show updated tag\n story.refreshTiddler(tagged[i].title,false,true);\n }\n }\n if (!this.tiddlerExists(title) &amp;&amp; newBody == &quot;&quot;) {\n // dont create unwanted tiddler\n return null;\n }\n }\n return this.saveTiddler_orig_tagrename(title,newTitle,newBody,modifier,modified,tags);\n}\n\n//}}}\n\n</div>
<div tiddler="Semi-Hosted mode" modifier="ashinw" modified="200608041908" created="200608041908" tags="Primer Architecture">~Semi-Hosted mode means running an application using an external Tomcat server whether it be on localhost or anywhere else.</div>
<div tiddler="SiteSubtitle" modifier="ashinw" modified="200608032316" created="200603080728" tags=""> - Gem based modifications</div>
<div tiddler="SiteTitle" modifier="ashinw" modified="200608032314" created="200603060505" tags="">Google Widget Toolkit - User library</div>
<div tiddler="Someday" modifier="SimonBaird" modified="200604261329" created="200604261329" tags=""></div>
<div tiddler="StyleSheet" modifier="AshinWima" modified="200606021835" created="200601061745" tags="">[[TagglyTaggingStyles]]\n\n/*{{{*/\nbody.twShell { \n font:small Verdana,Sans-serif;\n}\n\nimg {\n  border: 8px solid white;\n}\n\n.editor textarea { font-family:monospace; }\n\n.tiddler {\npadding-bottom:1em; margin-bottom:1em; \n background:#fff; padding-right:1.5em; \n}\n\ntwShell {position:static;}\n.tagClear{margin-top:1em;clear:both;}\n\n.header {\n position: relative; \n}\n\n.headerShadow {\n position: relative;\n padding: .5em .5em 1em .5em;\n}\n\n.headerForeground {\n position: absolute;\n padding: .5em .5em 1em .5em;\n}\n\n\n.siteTitle {\n font-size: 1.5em;\n}\n\n.siteSubtitle {\n font-size: 1em;\n}\n\n#tiddlerRecentTable td { \n vertical-align:top; \n font-size:80%;\n} \n\n#tiddlerWelcome td { \n vertical-align:top; \n font-size:80%;\n} \n\n\n#mainMenu {\n position: absolute;\n text-align: left;\n left: 0px;\n width: 11em;\n font-size: 1em;\n padding:2px;\n}\n\n\n#mainMenu .quickopentag { \n padding:0px; \n margin:0px; \n border:0px; \n}\n\n#mainMenu .quickopentag .button { \n padding-left:1px; \n margin-left:0px; \n border:0px; \n}\n\n/*}}}*/\n</div>
<div tiddler="StyleSheetColors" modifier="AshinWima" modified="200606020254" created="200606011608" tags="">/***\n!Generic Rules /%==============================================%/\n***/\n/*{{{*/\n\nbody.twShell {\nbackground: #99aa99;\n}\n\n#mainMenu {\nbackground: #bbccdd;\n}\n\n.button {\n\n}\n\na {\n color:#358;\n text-decoration:none;\n border-bottom:1px dotted #469;\n}\n\na:hover{\n color:#933;\n}\n\na img{\nborder: 0;\n}\n\nh1,h2,h3,h4,h5 {\ncolor: #335588;\n}\n\n.button {\n}\n\n.button:hover {\n}\n\n.button:active {\n}\n\n/*}}}*/\n/***\n!Header /%==================================================%/\n***/\n/*{{{*/\n.header {\n color:white;\n}\n\n.headerShadow {\n}\n\n.headerShadow a {\nfont-weight: normal;\n}\n\n.headerForeground {\n}\n\n.headerForeground a {\nfont-weight: normal;\n}\n\n/*}}}*/\n/***\n!General tabs /%=================================================%/\n***/\n/*{{{*/\n\n.tabSelected{\ncolor: #014;\nbackground: #fcfcfe;\n}\n\n.tabUnselected {\nbackground: #f2f2fd;\n}\n\n.tabContents {\nbackground: #eeeedd;\n}\n\n.tabContents .button {\n}\n\n/*}}}*/\n/***\n!Sidebar options /%=================================================%/\n~TiddlyLinks and buttons are treated identically in the sidebar and slider panel\n***/\n/*{{{*/\n#sidebar {\nbackground: #eeeedd;\n}\n\n#sidebarOptions input {\n}\n\n#sidebarOptions .sliderPanel {\n}\n\n#sidebarOptions .sliderPanel a {\n}\n\n#sidebarOptions .sliderPanel a:hover {\n}\n\n#sidebarOptions .sliderPanel a:active {\n}\n/*}}}*/\n/***\n!Message Area /%=================================================%/\n***/\n/*{{{*/\n#messageArea {\nbackground: #FAFAFB;\ncolor: #black;\nborder: 1px solid black;\n}\n\n#messageArea .button {\npadding: 0.2em 0.2em 0.2em 0.2em;\ncolor: #black;\nbackground: #f5f5f1;\n}\n\n/*}}}*/\n/***\n!Popup /%=================================================%/\n***/\n/*{{{*/\n.popup {\ncolor: black;\nbackground: #fff;\nborder: 1px solid black;\n}\n\n.popup hr {\n}\n\n.popup li.disabled {\ncolor: #999;\n}\n\n.popup li a, .popup li a:visited {\ncolor: black;\nborder: none;\n}\n\n.popup li a:hover {\nbackground: #112233;\ncolor: #fff;\nborder: none;\n}\n/*}}}*/\n/***\n!Tiddler Display /%=================================================%/\n***/\n/*{{{*/\n.tiddler .defaultCommand {\nfont-weight: normal;\n}\n\n.shadow .title {\n}\n\n.title {\n}\n\n.subtitle {\n}\n\n.toolbar {\n}\n\n.tagging, .tagged {\n}\n\n.selected .tagging, .selected .tagged {\n}\n\n.tagging .listTitle, .tagged .listTitle {\n}\n\n.tagging .button, .tagged .button {\n}\n\n.footer {\n}\n\n.selected .footer {\n}\n\n.sparkline {\n}\n\n.sparktick {\n}\n\n.errorButton {\n}\n\n.cascade {\n}\n\n.imageLink, #displayArea .imageLink {\nbackground: transparent;\n}\n\n/*}}}*/\n/***\n''The viewer is where the tiddler content is displayed'' /%------------------------------------------------%/\n***/\n/*{{{*/\n\n.viewer pre { \n background: #eeeedd; \n}\n\n.viewer .listTitle {list-style-type: none; margin-left: -2em;}\n\n.viewer .button {\n}\n\n.viewer blockquote {\n}\n\n.viewer table {\n}\n\n.viewer th, thead td {\n}\n\n.viewer td, .viewer tr {\n}\n\n.viewer code {\n}\n\n.viewer hr {\n}\n\n.highlight, .marked {\n}\n/*}}}*/\n/***\n''The editor replaces the viewer in the tiddler'' /%------------------------------------------------%/\n***/\n/*{{{*/\n.editor input {\n}\n\n.editor textarea {\nwidth: 100%;\n}\n\n.editorFooter {\n}\n\n.sparkline {\nbackground: #8cf;\n}\n.sparktick {\nbackground: #014;\n}\n/*}}}*/</div>
<div tiddler="StyleSheetLayout" modifier="AshinWima" modified="200606020259" created="200606011914" tags="">/***\n!Sections in this Tiddler:\n*Generic rules\n**Links styles\n**Link Exceptions\n*Header\n*Main menu\n*Sidebar\n**Sidebar options\n**Sidebar tabs\n*Message area\n*Popup\n*Tabs\n*Tiddler display\n**Viewer\n**Editor\n*Misc. rules\n!Generic Rules /%==============================================%/\n***/\n/*{{{*/\nbody.twShell {position:  relative;  margin: 0;  padding: 0;}\n\nh1,h2,h3,h4,h5 {\n font-weight: bold;\n text-decoration: none;\n padding-left: 0.4em;\n}\n\nh1 {font-size: 1.35em;}\nh2 {font-size: 1.25em;}\nh3 {font-size: 1.1em;}\nh4 {font-size: 1em;}\nh5 {font-size: .9em;}\n\nhr {\n height: 1px;\n}\n\na{\n text-decoration: none;\n}\n\nol { list-style-type: decimal }\nol ol { list-style-type: lower-alpha }\nol ol ol { list-style-type: lower-roman }\nol ol ol ol { list-style-type: decimal }\nol ol ol ol ol { list-style-type: lower-alpha }\nol ol ol ol ol ol { list-style-type: lower-roman }\nol ol ol ol ol ol ol { list-style-type: decimal }\n/*}}}*/\n/***\n''General Link Styles'' /%-----------------------------------------------------------------------------%/\n***/\n/*{{{*/\n.externalLink {\n text-decoration: underline;\n}\n\n.tiddlyLinkExisting {\n font-weight: bold;\n}\n\n.tiddlyLinkNonExisting {\n font-style: italic;\n}\n\n/* the 'a' is required for IE, otherwise it renders the whole tiddler a bold */\na.tiddlyLinkNonExisting.shadow {\n font-weight: bold;\n}\n/*}}}*/\n/***\n''Exceptions to common link styles'' /%------------------------------------------------------------------%/\n***/\n/*{{{*/\n\n#mainMenu .tiddlyLinkExisting, \n#mainMenu .tiddlyLinkNonExisting,\n#sidebarTabs .tiddlyLinkExisting,\n#sidebarTabs .tiddlyLinkNonExisting{\n font-weight: normal;\n font-style: normal;\n}\n\n/*}}}*/\n/***\n!Header /%==================================================%/\n***/\n/*{{{*/\n\n.header {\n position: relative;\n}\n\n.header a:hover {\n background: transparent;\n}\n\n.headerShadow {\n position: relative;\n padding: 4.5em 0em 1em 1em;\n left: -1px;\n top: -1px;\n}\n\n.headerForeground {\n position: absolute;\n padding: 4.5em 0em 1em 1em;\n left: 0px;\n top: 0px;\n}\n\n.siteTitle {\n font-size: 3em;\n}\n\n.siteSubtitle {\n font-size: 1.2em;\n}\n\n/*}}}*/\n/***\n!Main menu /%==================================================%/\n***/\n/*{{{*/\n#mainMenu {\n position: absolute;\n left: 0;\n width: 10em;\n text-align: right;\n line-height: 1.6em;\n padding: 1.5em 0.5em 0.5em 0.5em;\n font-size: 1.1em;\n}\n\n/*}}}*/\n/***\n!Sidebar rules /%==================================================%/\n***/\n/*{{{*/\n#sidebar {\n position: absolute;\n right: 3px;\n width: 16em;\n font-size: .9em;\n}\n/*}}}*/\n/***\n''Sidebar options'' /%----------------------------------------------------------------------------------%/\n***/\n/*{{{*/\n#sidebarOptions {\n padding-top: 0.3em;\n}\n\n#sidebarOptions a {\n margin: 0em 0.2em;\n padding: 0.2em 0.3em;\n display: block;\n}\n\n#sidebarOptions input {\n margin: 0.4em 0.5em;\n}\n\n#sidebarOptions .sliderPanel {\n margin-left: 1em;\n padding: 0.5em;\n font-size: .85em;\n}\n\n#sidebarOptions .sliderPanel a {\n font-weight: bold;\n display: inline;\n padding: 0;\n}\n\n#sidebarOptions .sliderPanel input {\n margin: 0 0 .3em 0;\n}\n/*}}}*/\n/***\n''Sidebar tabs'' /%-------------------------------------------------------------------------------------%/\n***/\n/*{{{*/\n\n#sidebarTabs .tabContents {\n width: 15em;\n overflow: hidden;\n}\n\n/*}}}*/\n/***\n!Message area /%==================================================%/\n***/\n/*{{{*/\n#messageArea {\nposition:absolute; top:0; right:0; margin: 0.5em; padding: 0.5em;\n}\n\n*[id='messageArea'] {\nposition:fixed !important; z-index:99;}\n\n.messageToolbar {\ndisplay: block;\ntext-align: right;\n}\n\n#messageArea a{\n text-decoration: underline;\n}\n/*}}}*/\n/***\n!Popup /%==================================================%/\n***/\n/*{{{*/\n.popup {\n font-size: .9em;\n padding: 0.2em;\n list-style: none;\n margin: 0;\n}\n\n.popup hr {\n display: block;\n height: 1px;\n width: auto;\n padding: 0;\n margin: 0.2em 0em;\n}\n\n.popup li.disabled {\n padding: 0.2em;\n}\n\n.popup li a{\n display: block;\n padding: 0.2em;\n}\n/*}}}*/\n/***\n!Tabs /%==================================================%/\n***/\n/*{{{*/\n.tabset {\n padding: 1em 0em 0em 0.5em;\n}\n\n.tab {\n margin: 0em 0em 0em 0.25em;\n padding: 2px;\n}\n\n.tabContents {\n padding: 0.5em;\n}\n\n.tabContents ul, .tabContents ol {\n margin: 0;\n padding: 0;\n}\n\n.txtMainTab .tabContents li {\n list-style: none;\n}\n\n.tabContents li.listLink {\n margin-left: .75em;\n}\n/*}}}*/\n/***\n!Tiddler display rules /%==================================================%/\n***/\n/*{{{*/\n#displayArea {\n margin: 1em 17em 0em 14em;\n}\n\n\n.toolbar {\n text-align: right;\n font-size: .9em;\n visibility: hidden;\n}\n\n.selected .toolbar {\n visibility: visible;\n}\n\n.tiddler {\n padding: 1em 1em 0em 1em;\n}\n\n.missing .viewer,.missing .title {\n font-style: italic;\n}\n\n.title {\n font-size: 1.6em;\n font-weight: bold;\n}\n\n.missing .subtitle {\n display: none;\n}\n\n.subtitle {\n font-size: 1.1em;\n}\n\n/* I'm not a fan of how button looks in tiddlers... */\n.tiddler .button {\n padding: 0.2em 0.4em;\n}\n\n.tagging {\nmargin: 0.5em 0.5em 0.5em 0;\nfloat: left;\ndisplay: none;\n}\n\n.isTag .tagging {\ndisplay: block;\n}\n\n.tagged {\nmargin: 0.5em;\nfloat: right;\n}\n\n.tagging, .tagged {\nfont-size: 0.9em;\npadding: 0.25em;\n}\n\n.tagging ul, .tagged ul {\nlist-style: none;margin: 0.25em;\npadding: 0;\n}\n\n.tagClear {\nclear: both;\n}\n\n.footer {\n font-size: .9em;\n}\n\n.footer li {\ndisplay: inline;\n}\n/***\n''The viewer is where the tiddler content is displayed'' /%------------------------------------------------%/\n***/\n/*{{{*/\n* html .viewer pre {\n width: 99%;\n padding: 0 0 1em 0;\n}\n\n.viewer {\n line-height: 1.4em;\n padding-top: 0.5em;\n}\n\n.viewer .button {\n margin: 0em 0.25em;\n padding: 0em 0.25em;\n}\n\n.viewer blockquote {\n line-height: 1.5em;\n padding-left: 0.8em;\n margin-left: 2.5em;\n}\n\n.viewer ul, .viewer ol{\n margin-left: 0.5em;\n padding-left: 1.5em;\n}\n\n.viewer table {\n margin: 0.8em 1.0em;\n}\n\n.viewer th, .viewer td, .viewer tr,.viewer caption{\n padding: 3px;\n}\n\n.viewer pre {\n padding: 0.5em;\n margin-left: 0.5em;\n font-size: 1.2em;\n line-height: 1.4em;\n overflow: auto;\n}\n\n.viewer code {\n font-size: 1.2em;\n line-height: 1.4em;\n}\n/*}}}*/\n/***\n''The editor replaces the viewer in the tiddler'' /%------------------------------------------------%/\n***/\n/*{{{*/\n.editor {\nfont-size: 1.1em;\n}\n\n.editor input, .editor textarea {\n display: block;\n width: 100%;\n font: inherit;\n}\n\n.editorFooter {\n padding: 0.25em 0em;\n font-size: .9em;\n}\n\n.editorFooter .button {\npadding-top: 0px; padding-bottom: 0px;}\n\n.fieldsetFix {b\n\npadding: 0;\nmargin: 1px 0px 1px 0px;\n}\n/*}}}*/\n/***\n!Misc rules /%==================================================%/\n***/\n/*{{{*/\n.sparkline {\n line-height: 1em;\n}\n\n.sparktick {\n outline: 0;\n}\n\n.zoomer {\n font-size: 1.1em;\n position: absolute;\n padding: 1em;\n}\n\n.cascade {\n font-size: 1.1em;\n position: absolute;\n overflow: hidden;\n}\n/*}}}*/</div>
<div tiddler="Submit Request for consideration to GWT Team" modifier="ashinw" modified="200608040322" created="200608040316" tags="Reminder [[Gems-GWT-User project roadmap]]">I anticipate that the official ~GWT-user distribution will mature and get to a level of acceptable stability within 6 more months. At that point in time I hope all Gem changes can get incorporated into the main GWT user release and that this distribution becomes obsolete. However, in order for that to happen the GWT team will need to make some adjustments on release cycles, visibility and transpareny and giving up some control and leveraging community contributions. Whether this will happen within 6 months or longer does not matter, the goal is to have one distribution that is satisfying the needs of the Gems/GWT community.\n\nFor each change, raise a change record which details and rationalises the request.</div>
<div tiddler="TagBasedTemplates" modifier="SimonBaird" modified="200603080033" created="200603080033" tags="systemConfig">/***\n| Name:|TagBasedTemplates|\n| Source:|http://simonbaird.com/mptw/#TagBasedTemplates|\n| Version:|1.0.1 (8-Mar-2006)|\n| Usage:|See [[FlipMeOver!]] for an example|\n\n!Notes\nIf there is more than one match the first one wins...\n\n!History\n* 1.0.1 (8-Mar-2006)\n** added format string\n* 1.0.0 (8-Mar-2006)\n** simplified to just look for existence of &quot;~TagNameViewTemplate&quot; as suggested by tomo on TiddlyWikiDev\n* Prototype (12-Jan-2006)\n\n***/\n//{{{\n\nversion.extensions.TagBasedTemplates = { major: 1, minor: 0, revision: 1, date: new Date(2006,3,8),\n source: &quot;http://simonbaird.com/mptw/#TagBasedTemplates&quot;\n};\n\nconfig.TagBasedTemplates = { templateFormat: &quot;%0ViewTemplate&quot; }; // in case you want to tweak it\n\nstory.chooseTemplateForTiddler = function(title,template) {\n if (!template) {\n var tiddler = store.getTiddler(title);\n if (tiddler)\n for (var j=0; j&lt;tiddler.tags.length; j++) {\n var lookFor = config.TagBasedTemplates.templateFormat.format([tiddler.tags[j]]);\n if (store.tiddlerExists(lookFor))\n return lookFor;\n }\n return config.tiddlerTemplates[DEFAULT_VIEW_TEMPLATE];\n }\n return config.tiddlerTemplates[template];\n};\n\n//}}}\n</div>
<div tiddler="TagglyListPlugin" modifier="SimonBaird" modified="200603080623" created="200601140102" tags="systemConfig">/***\n|Name|TagglyListPlugin|\n|Created by|SimonBaird|\n|Location|http://simonbaird.com/mptw/#TagglyListPlugin|\n|Version|1.1.1 6-Mar-06|\n|Requires|See TagglyTagging|\n\n!History\n* 1.1.1 (6-Mar-2006) fixed bug with refreshAllVisible closing tiddlers being edited. Thanks Luke Blanshard.\n\n***/\n\n/***\n!Setup and config\n***/\n//{{{\n\nversion.extensions.TagglyListPlugin = {\n major: 1, minor: 1, revision: 1,\n date: new Date(2006,3,6),\n source: &quot;http://simonbaird.com/mptw/#TagglyListPlugin&quot;\n};\n\nconfig.macros.tagglyList = {};\nconfig.macros.tagglyListByTag = {};\nconfig.macros.tagglyListControl = {};\nconfig.macros.tagglyListWithSort = {};\nconfig.macros.hideSomeTags = {};\n\n// change this to your preference\nconfig.macros.tagglyListWithSort.maxCols = 6;\n\nconfig.macros.tagglyList.label = &quot;Tagged as %0:&quot;;\n\n// the default sort options. set these to your preference\nconfig.macros.tagglyListWithSort.defaults = {\n sortBy:&quot;title&quot;, // title|created|modified\n sortOrder: &quot;asc&quot;, // asc|desc\n hideState: &quot;show&quot;, // show|hide\n groupState: &quot;nogroup&quot;, // nogroup|group\n numCols: 1\n};\n\n// these tags will be ignored by the grouped view\nconfig.macros.tagglyListByTag.excludeTheseTags = [\n &quot;systemConfig&quot;,\n &quot;TiddlerTemplates&quot;\n];\n\nconfig.macros.tagglyListControl.tags = {\n title:&quot;sortByTitle&quot;, \n modified: &quot;sortByModified&quot;, \n created: &quot;sortByCreated&quot;,\n asc:&quot;sortAsc&quot;, \n desc:&quot;sortDesc&quot;,\n hide:&quot;hideTagged&quot;, \n show:&quot;showTagged&quot;,\n nogroup:&quot;noGroupByTag&quot;,\n group:&quot;groupByTag&quot;,\n cols1:&quot;list1Cols&quot;,\n cols2:&quot;list2Cols&quot;,\n cols3:&quot;list3Cols&quot;,\n cols4:&quot;list4Cols&quot;,\n cols5:&quot;list5Cols&quot;,\n cols6:&quot;list6Cols&quot;,\n cols7:&quot;list7Cols&quot;,\n cols8:&quot;list8Cols&quot;,\n cols9:&quot;list9Cols&quot; \n}\n\n// note: should match config.macros.tagglyListControl.tags\nconfig.macros.hideSomeTags.tagsToHide = [\n &quot;sortByTitle&quot;,\n &quot;sortByCreated&quot;,\n &quot;sortByModified&quot;,\n &quot;sortDesc&quot;,\n &quot;sortAsc&quot;,\n &quot;hideTagged&quot;,\n &quot;showTagged&quot;,\n &quot;noGroupByTag&quot;,\n &quot;groupByTag&quot;,\n &quot;list1Cols&quot;,\n &quot;list2Cols&quot;,\n &quot;list3Cols&quot;,\n &quot;list4Cols&quot;,\n &quot;list5Cols&quot;,\n &quot;list6Cols&quot;,\n &quot;list7Cols&quot;,\n &quot;list8Cols&quot;,\n &quot;list9Cols&quot;,\n &quot;startCollapsed&quot;,\n &quot;Done&quot;,&quot;Next&quot;,&quot;1-Urgent&quot;,&quot;2-Now&quot;,&quot;3-Soon&quot;,&quot;4-Someday&quot;,&quot;TaskDashboard&quot;\n];\n\n\n//}}}\n/***\n\n!Utils\n***/\n//{{{\n// from Eric\nfunction isTagged(title,tag) {\n var t=store.getTiddler(title); if (!t) return false;\n return (t.tags.find(tag)!=null);\n}\n\n// from Eric\nfunction toggleTag(title,tag) {\n var t=store.getTiddler(title); if (!t || !t.tags) return;\n if (t.tags.find(tag)==null) t.tags.push(tag);\n else t.tags.splice(t.tags.find(tag),1);\n}\n\nfunction addTag(title,tag) {\n var t=store.getTiddler(title); if (!t || !t.tags) return;\n t.tags.push(tag);\n}\n\nfunction removeTag(title,tag) {\n var t=store.getTiddler(title); if (!t || !t.tags) return;\n if (t.tags.find(tag)!=null) t.tags.splice(t.tags.find(tag),1);\n}\n\n// from Udo\nArray.prototype.indexOf = function(item) {\n for (var i = 0; i &lt; this.length; i++) {\n if (this[i] == item) {\n return i;\n }\n }\n return -1;\n};\nArray.prototype.contains = function(item) {\n return (this.indexOf(item) &gt;= 0);\n}\n//}}}\n/***\n\n!tagglyList\ndisplays a list of tagged tiddlers. \nparameters are sortField and sortOrder\n***/\n//{{{\n\n// not used at the moment...\nfunction sortedListOfOtherTags(tiddler,thisTag) {\n var list = tiddler.tags.concat(); // so we are working on a clone..\n for (var i=0;i&lt;config.macros.hideSomeTags.tagsToHide.length;i++) {\n if (list.find(config.macros.hideSomeTags.tagsToHide[i]) != null)\n list.splice(list.find(config.macros.hideSomeTags.tagsToHide[i]),1); // remove hidden ones\n }\n for (var i=0;i&lt;config.macros.tagglyListByTag.excludeTheseTags.length;i++) {\n if (list.find(config.macros.tagglyListByTag.excludeTheseTags[i]) != null)\n list.splice(list.find(config.macros.tagglyListByTag.excludeTheseTags[i]),1); // remove excluded ones\n }\n list.splice(list.find(thisTag),1); // remove thisTag\n return '[[' + list.sort().join(&quot;]] [[&quot;) + ']]';\n}\n\nfunction sortHelper(a,b) {\n if (a == b) return 0;\n else if (a &lt; b) return -1;\n else return +1;\n}\n\nconfig.macros.tagglyListByTag.handler = function (place,macroName,params,wikifier,paramString,tiddler) {\n\n var sortBy = params[0] ? params[0] : &quot;title&quot;; \n var sortOrder = params[1] ? params[1] : &quot;asc&quot;;\n\n var result = store.getTaggedTiddlers(tiddler.title,sortBy);\n\n if (sortOrder == &quot;desc&quot;)\n result = result.reverse();\n\n var leftOvers = []\n for (var i=0;i&lt;result.length;i++) {\n leftOvers.push(result[i].title);\n }\n\n var allTagsHolder = {};\n for (var i=0;i&lt;result.length;i++) {\n for (var j=0;j&lt;result[i].tags.length;j++) {\n\n if ( \n result[i].tags[j] != tiddler.title // not this tiddler\n &amp;&amp; config.macros.hideSomeTags.tagsToHide.find(result[i].tags[j]) == null // not a hidden one\n &amp;&amp; config.macros.tagglyListByTag.excludeTheseTags.find(result[i].tags[j]) == null // not excluded\n ) {\n if (!allTagsHolder[result[i].tags[j]])\n allTagsHolder[result[i].tags[j]] = &quot;&quot;;\n allTagsHolder[result[i].tags[j]] += &quot;**[[&quot;+result[i].title+&quot;]]\sn&quot;;\n\n if (leftOvers.find(result[i].title) != null)\n leftOvers.splice(leftOvers.find(result[i].title),1); // remove from leftovers. at the end it will contain the leftovers...\n }\n }\n }\n\n\n var allTags = [];\n for (var t in allTagsHolder)\n allTags.push(t);\n\n allTags.sort(function(a,b) {\n var tidA = store.getTiddler(a);\n var tidB = store.getTiddler(b);\n if (sortBy == &quot;title&quot;) return sortHelper(a,b);\n else if (!tidA &amp;&amp; !tidB) return 0;\n else if (!tidA) return -1;\n else if (!tidB) return +1;\n else return sortHelper(tidA[sortBy],tidB[sortBy]);\n });\n\n var markup = &quot;&quot;;\n\n if (sortOrder == &quot;desc&quot;) {\n allTags.reverse();\n }\n else {\n // leftovers first...\n for (var i=0;i&lt;leftOvers.length;i++)\n markup += &quot;*[[&quot;+leftOvers[i]+&quot;]]\sn&quot;;\n } \n\n for (var i=0;i&lt;allTags.length;i++)\n markup += &quot;*[[&quot;+allTags[i]+&quot;]]\sn&quot; + allTagsHolder[allTags[i]];\n\n if (sortOrder == &quot;desc&quot;) {\n // leftovers last...\n for (var i=0;i&lt;leftOvers.length;i++)\n markup += &quot;*[[&quot;+leftOvers[i]+&quot;]]\sn&quot;;\n }\n\n wikify(markup,place);\n}\n\nconfig.macros.tagglyList.handler = function (place,macroName,params,wikifier,paramString,tiddler) {\n var sortBy = params[0] ? params[0] : &quot;title&quot;; \n var sortOrder = params[1] ? params[1] : &quot;asc&quot;;\n var numCols = params[2] ? params[2] : 1;\n\n var result = store.getTaggedTiddlers(tiddler.title,sortBy);\n if (sortOrder == &quot;desc&quot;)\n result = result.reverse();\n\n var listSize = result.length;\n var colSize = listSize/numCols;\n var remainder = listSize % numCols;\n\n var upperColsize;\n var lowerColsize;\n if (colSize != Math.floor(colSize)) {\n // it's not an exact fit so..\n lowerColsize = Math.floor(colSize);\n upperColsize = Math.floor(colSize) + 1;\n }\n else {\n lowerColsize = colSize;\n upperColsize = colSize;\n }\n\n var markup = &quot;&quot;;\n var c=0;\n\n var newTaggedTable = createTiddlyElement(place,&quot;table&quot;);\n var newTaggedBody = createTiddlyElement(newTaggedTable,&quot;tbody&quot;);\n var newTaggedTr = createTiddlyElement(newTaggedBody,&quot;tr&quot;);\n\n for (var j=0;j&lt;numCols;j++) {\n var foo = &quot;&quot;;\n var thisSize;\n\n if (j&lt;remainder)\n thisSize = upperColsize;\n else\n thisSize = lowerColsize;\n\n for (var i=0;i&lt;thisSize;i++) \n foo += ( &quot;*[[&quot; + result[c++].title + &quot;]]\sn&quot;); // was using splitList.shift() but didn't work in IE;\n\n var newTd = createTiddlyElement(newTaggedTr,&quot;td&quot;,null,&quot;tagglyTagging&quot;);\n wikify(foo,newTd);\n\n }\n\n};\n\n/* snip for later.....\n //var groupBy = params[3] ? params[3] : &quot;t.title.substr(0,1)&quot;;\n //var groupBy = params[3] ? params[3] : &quot;sortedListOfOtherTags(t,tiddler.title)&quot;;\n //var groupBy = params[3] ? params[3] : &quot;t.modified&quot;;\n var groupBy = null; // for now. groupBy here is working but disabled for now.\n\n var prevGroup = &quot;&quot;;\n var thisGroup = &quot;&quot;;\n\n if (groupBy) {\n result.sort(function(a,b) {\n var t = a; var aSortVal = eval(groupBy); var aSortVal2 = eval(&quot;t&quot;.sortBy);\n var t = b; var bSortVal = eval(groupBy); var bSortVal2 = eval(&quot;t&quot;.sortBy);\n var t = b; var bSortVal2 = eval(groupBy);\n return (aSortVal == bSortVal ?\n (aSortVal2 == bSortVal2 ? 0 : (aSortVal2 &lt; bSortVal2 ? -1 : +1)) // yuck\n : (aSortVal &lt; bSortVal ? -1 : +1));\n });\n }\n\n if (groupBy) {\n thisGroup = eval(groupBy);\n if (thisGroup != prevGroup)\n markup += &quot;*[[&quot;+thisGroup+']]\sn';\n markup += &quot;**[[&quot;+t.title+']]\sn';\n prevGroup = thisGroup;\n }\n\n\n\n*/\n\n\n//}}}\n\n/***\n\n!tagglyListControl\nUse to make the sort control buttons\n***/\n//{{{\n\nfunction getSortBy(title) {\n var tiddler = store.getTiddler(title);\n var defaultVal = config.macros.tagglyListWithSort.defaults.sortBy;\n if (!tiddler) return defaultVal;\n var usetags = config.macros.tagglyListControl.tags;\n if (tiddler.tags.contains(usetags[&quot;title&quot;])) return &quot;title&quot;;\n else if (tiddler.tags.contains(usetags[&quot;modified&quot;])) return &quot;modified&quot;;\n else if (tiddler.tags.contains(usetags[&quot;created&quot;])) return &quot;created&quot;;\n else return defaultVal;\n}\n\nfunction getSortOrder(title) {\n var tiddler = store.getTiddler(title);\n var defaultVal = config.macros.tagglyListWithSort.defaults.sortOrder;\n if (!tiddler) return defaultVal;\n var usetags = config.macros.tagglyListControl.tags;\n if (tiddler.tags.contains(usetags[&quot;asc&quot;])) return &quot;asc&quot;;\n else if (tiddler.tags.contains(usetags[&quot;desc&quot;])) return &quot;desc&quot;;\n else return defaultVal;\n}\n\nfunction getHideState(title) {\n var tiddler = store.getTiddler(title);\n var defaultVal = config.macros.tagglyListWithSort.defaults.hideState;\n if (!tiddler) return defaultVal;\n var usetags = config.macros.tagglyListControl.tags;\n if (tiddler.tags.contains(usetags[&quot;hide&quot;])) return &quot;hide&quot;;\n else if (tiddler.tags.contains(usetags[&quot;show&quot;])) return &quot;show&quot;;\n else return defaultVal;\n}\n\nfunction getGroupState(title) {\n var tiddler = store.getTiddler(title);\n var defaultVal = config.macros.tagglyListWithSort.defaults.groupState;\n if (!tiddler) return defaultVal;\n var usetags = config.macros.tagglyListControl.tags;\n if (tiddler.tags.contains(usetags[&quot;group&quot;])) return &quot;group&quot;;\n else if (tiddler.tags.contains(usetags[&quot;nogroup&quot;])) return &quot;nogroup&quot;;\n else return defaultVal;\n}\n\nfunction getNumCols(title) {\n var tiddler = store.getTiddler(title);\n var defaultVal = config.macros.tagglyListWithSort.defaults.numCols; // an int\n if (!tiddler) return defaultVal;\n var usetags = config.macros.tagglyListControl.tags;\n for (var i=1;i&lt;=config.macros.tagglyListWithSort.maxCols;i++)\n if (tiddler.tags.contains(usetags[&quot;cols&quot;+i])) return i;\n return defaultVal;\n}\n\n\nfunction getSortLabel(title,which) {\n // TODO. the strings here should be definable in config\n var by = getSortBy(title);\n var order = getSortOrder(title);\n var hide = getHideState(title);\n var group = getGroupState(title);\n if (which == &quot;hide&quot;) return (hide == &quot;show&quot; ? &quot;?&quot; : &quot;+&quot;); // 0x25b8;\n else if (which == &quot;group&quot;) return (group == &quot;group&quot; ? &quot;normal&quot; : &quot;grouped&quot;);\n else if (which == &quot;cols&quot;) return &quot;cols�&quot;; // &amp;plusmn;\n else if (by == which) return which + (order == &quot;asc&quot; ? &quot;?&quot; : &quot;?&quot;); // &amp;uarr; &amp;darr;\n else return which;\n}\n\nfunction handleSortClick(title,which) {\n var currentSortBy = getSortBy(title);\n var currentSortOrder = getSortOrder(title);\n var currentHideState = getHideState(title);\n var currentGroupState = getGroupState(title);\n var currentNumCols = getNumCols(title);\n\n var tags = config.macros.tagglyListControl.tags;\n\n // if it doesn't exist, lets create it..\n if (!store.getTiddler(title))\n store.saveTiddler(title,title,&quot;&quot;,config.options.txtUserName,new Date(),null);\n\n if (which == &quot;hide&quot;) {\n // toggle hide state\n var newHideState = (currentHideState == &quot;hide&quot; ? &quot;show&quot; : &quot;hide&quot;);\n removeTag(title,tags[currentHideState]);\n if (newHideState != config.macros.tagglyListWithSort.defaults.hideState)\n toggleTag(title,tags[newHideState]);\n }\n else if (which == &quot;group&quot;) {\n // toggle hide state\n var newGroupState = (currentGroupState == &quot;group&quot; ? &quot;nogroup&quot; : &quot;group&quot;);\n removeTag(title,tags[currentGroupState]);\n if (newGroupState != config.macros.tagglyListWithSort.defaults.groupState)\n toggleTag(title,tags[newGroupState]);\n }\n else if (which == &quot;cols&quot;) {\n // toggle num cols\n var newNumCols = currentNumCols + 1; // confusing. currentNumCols is an int\n if (newNumCols &gt; config.macros.tagglyListWithSort.maxCols || newNumCols &gt; store.getTaggedTiddlers(title).length)\n newNumCols = 1;\n removeTag(title,tags[&quot;cols&quot;+currentNumCols]);\n if ((&quot;cols&quot;+newNumCols) != config.macros.tagglyListWithSort.defaults.groupState)\n toggleTag(title,tags[&quot;cols&quot;+newNumCols]);\n }\n else if (currentSortBy == which) {\n // toggle sort order\n var newSortOrder = (currentSortOrder == &quot;asc&quot; ? &quot;desc&quot; : &quot;asc&quot;);\n removeTag(title,tags[currentSortOrder]);\n if (newSortOrder != config.macros.tagglyListWithSort.defaults.sortOrder)\n toggleTag(title,tags[newSortOrder]);\n }\n else {\n // change sortBy only\n removeTag(title,tags[&quot;title&quot;]);\n removeTag(title,tags[&quot;created&quot;]);\n removeTag(title,tags[&quot;modified&quot;]);\n\n if (which != config.macros.tagglyListWithSort.defaults.sortBy)\n toggleTag(title,tags[which]);\n }\n\n store.setDirty(true); // save is required now.\n story.refreshTiddler(title,false,true); // force=true\n}\n\nconfig.macros.tagglyListControl.handler = function (place,macroName,params,wikifier,paramString,tiddler) {\n var onclick = function(e) {\n if (!e) var e = window.event;\n handleSortClick(tiddler.title,params[0]);\n e.cancelBubble = true;\n if (e.stopPropagation) e.stopPropagation();\n return false;\n };\n createTiddlyButton(place,getSortLabel(tiddler.title,params[0]),&quot;Click to change sort options&quot;,onclick,params[0]==&quot;hide&quot;?&quot;hidebutton&quot;:&quot;button&quot;);\n}\n//}}}\n/***\n\n!tagglyListWithSort\nput it all together..\n***/\n//{{{\nconfig.macros.tagglyListWithSort.handler = function (place,macroName,params,wikifier,paramString,tiddler) {\n if (tiddler &amp;&amp; store.getTaggedTiddlers(tiddler.title).length &gt; 0)\n // todo make this readable\n wikify(\n &quot;&lt;&lt;tagglyListControl hide&gt;&gt;&quot;+\n (getHideState(tiddler.title) != &quot;hide&quot; ? \n '&lt;html&gt;&lt;span class=&quot;tagglyLabel&quot;&gt;'+config.macros.tagglyList.label.format([tiddler.title])+' &lt;/span&gt;&lt;/html&gt;'+\n &quot;&lt;&lt;tagglyListControl title&gt;&gt;&lt;&lt;tagglyListControl modified&gt;&gt;&lt;&lt;tagglyListControl created&gt;&gt;&lt;&lt;tagglyListControl group&gt;&gt;&quot;+(getGroupState(tiddler.title)==&quot;group&quot;?&quot;&quot;:&quot;&lt;&lt;tagglyListControl cols&gt;&gt;&quot;)+&quot;\sn&quot; + \n &quot;&lt;&lt;tagglyList&quot; + (getGroupState(tiddler.title)==&quot;group&quot;?&quot;ByTag &quot;:&quot; &quot;) + getSortBy(tiddler.title)+&quot; &quot;+getSortOrder(tiddler.title)+&quot; &quot;+getNumCols(tiddler.title)+&quot;&gt;&gt;&quot; // hacky\n // + \sn----\sn&quot; +\n //&quot;&lt;&lt;tagglyList &quot;+getSortBy(tiddler.title)+&quot; &quot;+getSortOrder(tiddler.title)+&quot;&gt;&gt;&quot;\n : &quot;&quot;),\n place,null,tiddler);\n}\n\n//}}}\n/***\n\n!hideSomeTags\nSo we don't see the sort tags.\n(note, they are still there when you edit. Will that be too annoying?\n***/\n//{{{\n\n// based on tags.handler\nconfig.macros.hideSomeTags.handler = function(place,macroName,params,wikifier,paramString,tiddler) {\n var theList = createTiddlyElement(place,&quot;ul&quot;);\n if(params[0] &amp;&amp; store.tiddlerExists[params[0]])\n tiddler = store.getTiddler(params[0]);\n var lingo = config.views.wikified.tag;\n var prompt = tiddler.tags.length == 0 ? lingo.labelNoTags : lingo.labelTags;\n createTiddlyElement(theList,&quot;li&quot;,null,&quot;listTitle&quot;,prompt.format([tiddler.title]));\n for(var t=0; t&lt;tiddler.tags.length; t++)\n if (!this.tagsToHide.contains(tiddler.tags[t])) // this is the only difference from tags.handler...\n createTagButton(createTiddlyElement(theList,&quot;li&quot;),tiddler.tags[t],tiddler.title);\n\n}\n\n//}}}\n/***\n\n!Refresh everything when we save a tiddler. So the tagged lists never get stale. Is this too slow???\n***/\n//{{{\n\nfunction refreshAllVisible() {\n story.forEachTiddler(function(title,element) {\n if (element.getAttribute(&quot;dirty&quot;) != &quot;true&quot;) \n story.refreshTiddler(title,false,true);\n });\n}\n\nstory.saveTiddler_orig_mptw = story.saveTiddler;\nstory.saveTiddler = function(title,minorUpdate) {\n var result = this.saveTiddler_orig_mptw(title,minorUpdate);\n refreshAllVisible();\n return result;\n}\n\nstore.removeTiddler_orig_mptw = store.removeTiddler;\nstore.removeTiddler = function(title) {\n this.removeTiddler_orig_mptw(title);\n refreshAllVisible();\n}\n\n//}}}\n\n// // &lt;html&gt;&amp;#x25b8;&amp;#x25be;&amp;minus;&amp;plusmn;&lt;/html&gt;</div>
<div tiddler="TagglyTaggingStyles" modifier="SimonBaird" modified="200603060429" created="200601101205" tags="">/***\nTo use, add {{{[[TagglyTaggingStyles]]}}} to your StyleSheet tiddler, or you can just paste the CSS in directly. See also ViewTemplate, EditTemplate and TagglyTagging.\n***/\n/*{{{*/\n.tagglyTagged li.listTitle { display:none;}\n.tagglyTagged li { display: inline; font-size:90%; }\n.tagglyTagged ul { margin:0px; padding:0px; }\n.tagglyTagging { padding-top:0.5em; }\n.tagglyTagging li.listTitle { display:none;}\n.tagglyTagging ul { margin-top:0px; padding-top:0.5em; padding-left:2em; margin-bottom:0px; padding-bottom:0px; }\n\n/* .tagglyTagging .tghide { display:inline; } */\n\n.tagglyTagging { vertical-align: top; margin:0px; padding:0px; }\n.tagglyTagging table { margin:0px; padding:0px; }\n\n\n.tagglyTagging .button { display:none; margin-left:3px; margin-right:3px; }\n.tagglyTagging .button, .tagglyTagging .hidebutton { color:#aaa; font-size:90%; border:0px; padding-left:0.3em;padding-right:0.3em;}\n.tagglyTagging .button:hover, .hidebutton:hover { background:#eee; color:#888; }\n.selected .tagglyTagging .button { display:inline; }\n\n.tagglyTagging .hidebutton { color:white; } /* has to be there so it takes up space */\n.selected .tagglyTagging .hidebutton { color:#aaa }\n\n.tagglyLabel { color:#aaa; font-size:90%; }\n\n.tagglyTagging ul {padding-top:0px; padding-bottom:0.5em; margin-left:1em; }\n.tagglyTagging ul ul {list-style-type:disc; margin-left:-1em;}\n.tagglyTagging ul ul li {margin-left:0.5em; }\n\n.editLabel { font-size:90%; padding-top:0.5em; }\n/*}}}*/\n</div>
<div tiddler="Task" modifier="SimonBaird" modified="200603080634" created="200603080634" tags=""></div>
<div tiddler="TaskDashboardViewTemplate" modifier="YourName" modified="200605070807" created="200603080038" tags="ViewTemplates">&lt;!---\n| Name:|TaskDashboardViewTemplate |\n| Version:||\n| Source:|http://simonbaird.com/mptw/|\n---&gt;\n&lt;!--{{{--&gt;\n&lt;div class=&quot;toolbar&quot; macro=&quot;toolbar -closeTiddler closeOthers +editTiddler permalink references jump newHere&quot;&gt;&lt;/div&gt;\n&lt;div class=&quot;tagglyTagged&quot; macro=&quot;hideSomeTags&quot;&gt;&lt;/div&gt;\n&lt;div&gt;&lt;span class=&quot;title&quot; macro=&quot;view title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;miniTag&quot; macro=&quot;miniTag&quot;&gt;&lt;/span&gt;&lt;/div&gt;\n&lt;div class='subtitle'&gt;Created &lt;span macro='view created date [[DD/MM/YY]]'&gt;&lt;/span&gt;, updated &lt;span macro='view modified date [[DD/MM/YY]]'&gt;&lt;/span&gt;&lt;/div&gt;\n&lt;div class=&quot;viewer&quot; macro=&quot;view text wikified&quot;&gt;&lt;/div&gt;\n&lt;table width=&quot;100%&quot;&gt;&lt;tr&gt;\n\n&lt;td valign=&quot;top&quot; style=&quot;font-size:90%;border-right:1px dashed #888;padding:0.5em;&quot;&gt;\n&lt;xmp macro=&quot;wikifyContents&quot; class=&quot;viewer&quot;&gt;\n[[Contexts|Context]] &lt;&lt;newerTiddler button:&quot;new&quot; tags:&quot;Context&quot; name:&quot;New Context&quot; text:&quot;Enter details&quot;&gt;&gt;\n&lt;&lt;forEachTiddler\n where 'tiddler.tags.contains(&quot;Context&quot;)'\n sortBy tiddler.title\n write '&quot;*[[&quot;+tiddler.title+&quot;]] &quot;+\n/// display a count (by Clint)\n&quot;&lt;&lt;forEachTiddler where \sn&quot; +\n &quot; \s'tiddler.tags.containsAll([\s&quot;Task\s&quot;,\s&quot;&quot;+tiddler.title+&quot;\s&quot;]) &amp;&amp; &quot;+\n &quot; !tiddler.tags.contains(\s&quot;Done\s&quot;)\s'\sn&quot; +\n &quot; script \s'function writeTotalTasks(index, count) {if (index == 0) return \s&quot;(\s&quot;+count+\s&quot;)\s&quot;; else return \s&quot;\s&quot;;}\s' &quot;+\n &quot;write \s'writeTotalTasks(index,count)\s'$))&quot; +\n/// end display a count\n &quot;\sn&quot;' \n&gt;&gt;\n----\n[[Projects|Project]] &lt;&lt;newerTiddler button:&quot;new&quot; tags:&quot;Project&quot; name:&quot;New Project&quot; text:&quot;Enter details&quot;&gt;&gt;\n&lt;&lt;forEachTiddler \n where 'tiddler.tags.contains(&quot;Project&quot;)'\n write '&quot;*[[&quot;+tiddler.title+&quot;]] &quot;+\n/// display a count (by Clint)\n&quot;&lt;&lt;forEachTiddler where \sn&quot; +\n &quot; \s'tiddler.tags.containsAll([\s&quot;Task\s&quot;,\s&quot;&quot;+tiddler.title+&quot;\s&quot;]) &amp;&amp; &quot;+\n &quot; !tiddler.tags.contains(\s&quot;Done\s&quot;)\s'\sn&quot; +\n &quot; script \s'function writeTotalTasks(index, count) {if (index == 0) return \s&quot;(\s&quot;+count+\s&quot;)\s&quot;; else return \s&quot;\s&quot;;}\s' &quot;+\n &quot;write \s'writeTotalTasks(index,count)\s'$))&quot; +\n/// end display a count\n &quot;\sn&quot;'\n&gt;&gt;\n----\n~~[[Manage Contexts|Context]]~~\n&lt;/xmp&gt;\n&lt;/td&gt;\n\n\n&lt;td valign=&quot;top&quot; style=&quot;font-size:90%;border-right:1px dashed #888;padding:0.5em;&quot;&gt;\n&lt;xmp macro=&quot;wikifyContents&quot; class=&quot;viewer&quot;&gt;\n{div{nextAction{[[Next Actions|Next]] \s\n&lt;&lt;forEachTiddler\n where 'tiddler.tags.containsAll([&quot;Task&quot;,&quot;Next&quot;]) &amp;&amp; !tiddler.tags.contains(&quot;Done&quot;)'&gt;&gt;}}}\n{div{nextAction{[[Waiting For|Wait]] \s\n&lt;&lt;forEachTiddler\n where 'tiddler.tags.containsAll([&quot;Task&quot;,&quot;Wait&quot;]) &amp;&amp; !tiddler.tags.contains(&quot;Done&quot;)'&gt;&gt;}}}\n[[Done]] \s\n{div{scrolling{\s\n&lt;&lt;forEachTiddler\n where 'tiddler.tags.containsAll([&quot;Task&quot;]) &amp;&amp; tiddler.tags.contains(&quot;Done&quot;)'\n sortBy 'tiddler.modified' descending&gt;&gt;\s\n}}}\n&lt;/xmp&gt;\n&lt;/td&gt;\n\n\n&lt;td valign=&quot;top&quot; style=&quot;font-size:90%;padding:0.5em;&quot;&gt;\n&lt;table&gt;&lt;tr&gt;\n&lt;xmp macro=&quot;wikifyContents&quot; class=&quot;viewer&quot;&gt;\nAll [[Tasks|Task]] +++\n&lt;&lt;forEachTiddler\n where 'tiddler.tags.contains(&quot;Task&quot;) &amp;&amp; !tiddler.tags.contains(&quot;Done&quot;)'\n sortBy 'tiddler.title'\n write '&quot;@@font-size:90%;padding-left:0.5em;[[&quot; + tiddler.title + &quot;]]@@ &quot; + &quot;\sn&quot;'\n&gt;&gt;\n&lt;/xmp&gt;\n&lt;/tr&gt;\n&lt;table&gt;&lt;tr&gt;\n&lt;xmp macro=&quot;wikifyContents&quot; class=&quot;viewer&quot;&gt;\n----\n[[Someday/Maybe|Someday]] +++\n&lt;&lt;forEachTiddler\n where 'tiddler.tags.containsAll([&quot;Task&quot;, &quot;Someday&quot;]) &amp;&amp; !tiddler.tags.contains(&quot;Done&quot;)'\n sortBy 'tiddler.title'\n write '&quot;@@font-size:90%;padding-left:0.5em;[[&quot; + tiddler.title + &quot;]]@@ &quot; + &quot;\sn&quot;'\n&gt;&gt;\n&lt;/xmp&gt;\n&lt;/tr&gt;\n&lt;tr&gt;\n&lt;xmp macro=&quot;wikifyContents&quot; class=&quot;viewer&quot;&gt;\n----\n[[Reminders|Reminder]] &lt;&lt;newerTiddler button:&quot;new&quot; tags:&quot;Reminder&quot; name:&quot;New Reminder&quot; text:&quot;newReminder$))&quot;&gt;&gt;++++\n&lt;&lt;showReminders format:&quot;*DIFF, TITLE&quot;&gt;&gt;===\n\n&lt;/xmp&gt;\n&lt;/tr&gt;&lt;/table&gt;\n&lt;/td&gt;\n&lt;/tr&gt;&lt;/table&gt;\n&lt;br class=&quot;tagClear&quot;/&gt;\n&lt;div class=&quot;tagglyTagging&quot; macro=&quot;tagglyListWithSort&quot;&gt;&lt;/div&gt;\n\n\n&lt;!--\n\nexperiments...\n\n/% this one is very slick but I want self contained templates %/\n/%\n&lt;&lt;forEachTiddler\n where 'tiddler.tags.contains(&quot;Priority&quot;)'\n sortBy 'tiddler.title'\n write\n '(index==0?&quot;&lt;&lt;tabs txtTasksTab\sn&quot;:&quot;&quot;) + \n tiddler.title + &quot; &quot; + tiddler.title + &quot; &quot; + tiddler.title+&quot;\sn&quot; + \n (index==count-1?&quot;$))\sn&quot;:&quot;&quot;)' \n&gt;&gt;\n%/\n\n/% try nested sliders... %/\n!!Tasks\n{div{scrolling{\s\n&lt;&lt;forEachTiddler\n where 'tiddler.tags.contains(&quot;Priority&quot;)'\n sortBy 'tiddler.title'\n write\n '&quot;++++[&quot; + tiddler.title.substr(2) + &quot;]&quot; +\n &quot;&lt;&lt;forEachTiddler where \sn&quot; +\n &quot; \s'tiddler.tags.containsAll([\s&quot;Task\s&quot;,\s&quot;&quot;+tiddler.title+&quot;\s&quot;])\s'\sn&quot; +\n &quot;$))&quot; +\n &quot;===\sn\sn&quot;'\n&gt;&gt;\n}}}\n\n\n--&gt;\n\n\n&lt;!--}}}--&gt;\n\n\n</div>
<div tiddler="TaskListTemplate" modifier="SimonBaird" modified="200603070022" created="200603060541" tags="">&lt;&lt;newerTiddler button:&quot;New&quot; tags:&quot;Task [[$$]]&quot; name:&quot;New Task&quot;&gt;&gt;\n&lt;&lt;forEachTiddler\n where\n 'tiddler.tags.contains(&quot;Task&quot;) &amp;&amp; !tiddler.tags.contains(&quot;Done&quot;) &amp;&amp; tiddler.tags.contains(&quot;$$&quot;)'\n&gt;&gt;</div>
<div tiddler="TaskViewTemplate" modifier="GregWeber" modified="200604240233" created="200603080446" tags="ViewTemplates">&lt;!--{{{--&gt;\n&lt;div class=&quot;toolbar&quot; macro=&quot;toolbar -closeTiddler closeOthers +editTiddler permalink references jump newHere&quot;&gt;&lt;/div&gt;\n&lt;div class=&quot;tagglyTagged&quot; macro=&quot;hideSomeTags&quot;&gt;&lt;/div&gt;\n&lt;div&gt;&lt;span class=&quot;title&quot; macro=&quot;view title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;miniTag&quot; macro=&quot;miniTag&quot;&gt;&lt;/span&gt;&lt;/div&gt;\n&lt;xmp style=&quot;padding:0.5em;border:1px solid #ddd;font-size:90%;&quot; macro=&quot;wikifyContents&quot;&gt;\n&lt;&lt;toggleTag Next&gt;&gt;&lt;&lt;toggleTag Wait&gt;&gt;&lt;&lt;toggleTag Someday&gt;&gt;&lt;&lt;toggleTag Done&gt;&gt;\n&lt;&lt;forEachTiddler where 'tiddler.tags.contains(&quot;Context&quot;)' write\n '&quot;&lt;&lt;toggleTag [[&quot;+tiddler.title+&quot;]]$))&quot;'&gt;&gt;\n&lt;/xmp&gt;\n&lt;div class='subtitle'&gt;Created &lt;span macro='view created date [[DD/MM/YY]]'&gt;&lt;/span&gt;, updated &lt;span style=&quot;border:1px solid #ddd&quot; macro='view modified date [[DD/MM/YY]]'&gt;&lt;/span&gt;&lt;/div&gt;\n&lt;div class=&quot;viewer&quot; macro=&quot;view text wikified&quot;&gt;&lt;/div&gt;\n\n&lt;!-- Ken wanted a toolbar down here also. uncomment the following --&gt;\n&lt;!--\n&lt;br/&gt;\n&lt;br/&gt;&lt;xmp style=&quot;padding:0.5em;border:1px solid #ddd;font-size:90%;&quot; macro=&quot;wikifyContents&quot;&gt;\n&lt;&lt;toggleTag Next&gt;&gt;&lt;&lt;toggleTag Waiting&gt;&gt;&lt;&lt;toggleTag Done&gt;&gt; | \s\n&lt;&lt;forEachTiddler where 'tiddler.tags.contains(&quot;Context&quot;)' write\n '&quot;&lt;&lt;toggleTag [[&quot;+tiddler.title+&quot;]]$))&quot;'&gt;&gt;\n&lt;/xmp&gt;\n--&gt;\n&lt;div class=&quot;tagglyTagging&quot; macro=&quot;tagglyListWithSort&quot;&gt;&lt;/div&gt;\n&lt;!--}}}--&gt;\n</div>
<div tiddler="ToggleTagMacro" modifier="SimonBaird" modified="200603080447" created="200603080447" tags="systemConfig">/***\nExamples:\n\n|Code|Description|Example|h\n|{{{&lt;&lt;toggleTag&gt;&gt;}}}|Toggles the default tag (checked) in this tiddler|&lt;&lt;toggleTag&gt;&gt;|\n|{{{&lt;&lt;toggleTag TagName&gt;&gt;}}}|Toggles the TagName tag in this tiddler|&lt;&lt;toggleTag TagName&gt;&gt;|\n|{{{&lt;&lt;toggleTag TagName TiddlerName&gt;&gt;}}}|Toggles the TagName tag in the TiddlerName tiddler|&lt;&lt;toggleTag TagName TiddlerName&gt;&gt;|\n|{{{&lt;&lt;toggleTag TagName TiddlerName nolabel&gt;&gt;Click me}}}|Same but hide the label|&lt;&lt;toggleTag TagName TiddlerName nolabel&gt;&gt;Click me|\n(Note if TiddlerName doesn't exist it will be silently created)\n\n!Known issues\n* Doesn't smoothly handle the case where you toggle a tag in a tiddler that is current open for editing. Should it stick the tag in the edit box?\n\n!Code\n***/\n//{{{\n\n\n// This function contributed by Eric Shulman\nfunction toggleTag(title,tag) {\n var t=store.getTiddler(title); if (!t || !t.tags) return;\n if (t.tags.find(tag)==null) t.tags.push(tag)\n else t.tags.splice(t.tags.find(tag),1)\n}\n\n// This function contributed by Eric Shulman\nfunction isTagged(title,tag) {\n var t=store.getTiddler(title); if (!t) return false;\n return (t.tags.find(tag)!=null);\n}\n\nconfig.macros.toggleTag = {};\nconfig.views.wikified.toggleTag = {fulllabel: &quot;[[%0]] [[%1]]&quot;, shortlabel: &quot;[[%0]]&quot;, nolabel: &quot;&quot; };\n\nconfig.macros.toggleTag.handler = function(place,macroName,params,wikifier,paramString,tiddler) {\n if(tiddler instanceof Tiddler) {\n var tag = (params[0] &amp;&amp; params[0] != '.') ? params[0] : &quot;checked&quot;;\n var title = (params[1] &amp;&amp; params[1] != '.') ? params[1] : tiddler.title;\n var hidelabel = params[2]?true:false;\n\n var onclick = function(e) {\n if (!e) var e = window.event;\n if (!store.getTiddler(title))\n store.saveTiddler(title,title,&quot;&quot;,config.options.txtUserName,new Date(),null);\n toggleTag(title,tag);\n\n store.setDirty(true); // so TW knows it has to save now\n\n story.forEachTiddler(function(title,element) {\n if (element.getAttribute(&quot;dirty&quot;) != &quot;true&quot;) \n story.refreshTiddler(title,false,true);\n });\n\n return false;\n };\n\n var lingo = config.views.wikified.toggleTag;\n\n // this part also contributed by Eric Shulman\n var c = document.createElement(&quot;input&quot;);\n c.setAttribute(&quot;type&quot;,&quot;checkbox&quot;);\n c.onclick=onclick;\n place.appendChild(c);\n c.checked=isTagged(title,tag);\n\n if (!hidelabel) {\n var label = (title!=tiddler.title)?lingo.fulllabel:lingo.shortlabel;\n wikify(label.format([tag,title]),place);\n }\n }\n}\n\n//}}}\n</div>
<div tiddler="ViewTemplate" modifier="SimonBaird" modified="200603080727" created="200601131226" tags="ViewTemplates">&lt;!---\n| Name:|~TagglyTaggingViewTemplate |\n| Version:|1.2 (16-Jan-2006)|\n| Source:|http://simonbaird.com/mptw/#TagglyTaggingViewTemplate|\n| Purpose:|See TagglyTagging for more info|\n| Requires:|You need the CSS in TagglyTaggingStyles to make it look right|\n!History\n* 16-Jan-06, version 1.2, added tagglyListWithSort\n* 12-Jan-06, version 1.1, first version\n!Notes\nRemove the miniTag if you don't like it or you don't use QuickOpenTagPlugin\n---&gt;\n&lt;!--{{{--&gt;\n&lt;div class=&quot;toolbar&quot; macro=&quot;toolbar -closeTiddler closeOthers +editTiddler permalink references jump newHere&quot;&gt;&lt;/div&gt;\n&lt;div class=&quot;tagglyTagged&quot; macro=&quot;hideSomeTags&quot;&gt;&lt;/div&gt;\n&lt;div&gt;&lt;span class=&quot;title&quot; macro=&quot;view title&quot;&gt;&lt;/span&gt;&lt;span class=&quot;miniTag&quot; macro=&quot;miniTag&quot;&gt;&lt;/span&gt;&lt;/div&gt;\n&lt;div class='subtitle'&gt;Created &lt;span macro='view created date [[DD/MM/YY]]'&gt;&lt;/span&gt;, updated &lt;span macro='view modified date [[DD/MM/YY]]'&gt;&lt;/span&gt;&lt;/div&gt;\n&lt;div class=&quot;viewer&quot; macro=&quot;view text wikified&quot;&gt;&lt;/div&gt;\n&lt;div class=&quot;tagglyTagging&quot; macro=&quot;tagglyListWithSort&quot;&gt;&lt;/div&gt;\n&lt;!--}}}--&gt;\n</div>
<div tiddler="Wait" modifier="SimonBaird" modified="200604261329" created="200604261329" tags=""></div>
<div tiddler="Welcome" modifier="ashinw" modified="200608051928" created="200606012118" tags="Main">&lt;&lt;tiddler 'Latest news headline'&gt;&gt;\n&lt;&lt;tiddler 'Welcome table of contents'&gt;&gt;</div>
<div tiddler="Welcome table of contents" modifier="ashinw" modified="200608041955" created="200606012111" tags="Main">|bgcolor(#AABBCC):Elsewhere|bgcolor(#BBCCDD):Behind the scenes|\n|bgcolor(#DDDDEE):&lt;&lt;tiddler 'Aggregated links'&gt;&gt;|&lt;&lt;tiddler 'Behind the scenes'&gt;&gt;|</div>
<div tiddler="Whats in this release" modifier="ashinw" modified="200608091342" created="200608040026" tags="architecture">!What is in this release?\nThis release includes a couple of bug fixes and enhancements:\n- changed visibility of RemoteServiceServlet::isImplementedRemoteServiceInterface() method from private to protected\n- added RemoteServiceServlet::getObjectToInvokeServiceIntfMethodOn() method to enable StrutsAction style pluggability\n- changed RemoteServiceServlet::processCall() method to get target from getObjectToInvokeServiceIntfMethodOn()\n- added dblclick support to DOMImplIE6::init() to avoid double clicks from getting lost\n- changed visibility of HistoryImplIE6 from packaged to public to support override of history implementation\n- added configurability of history resource location on HistoryImplIE6 by introducing $wnd.__gwt_historyResourceLocation variable. change effected the init() and newItem() methods\n- changed visibility of HistoryImplSafari from packaged to public to support override of history implementation\n- added configurability of history resource location on HistoryImplSafari by introducing $wnd.__gwt_historyResourceLocation variable. change effected the init() and newItem() methods\n- set widget = null; in SimplePanel::remove() method to avoid a precondition that can never be satisfied once the first widget is added (ie. side-effect)\n- changed overrideability of RemoteServiceServlet::doPost (ie. removed final)\n- changed RemoteServiceServlet::respondWithFailure to be public static with a servlet context passed in such that filters can also call it\n- changed RemoteServiceServlet::doPost to honour the original http servlet do post contract to less restricted overrides\n- changed the DOMImplStandard::getParent() to check whether it is not null before attempting to modify a member variable</div>
<div tiddler="Why another distribution" modifier="ashinw" modified="200608040103" created="200608040024" tags="architecture">!Why did you create a separate distribution of the ~GWT-User library?\nAnswer:\n# There were a number of defects within the library that needed to fixed. These defects were show stoppers for my project's release plans\n# I needed to open the library up more to be able to leverage it and solve the problems that I faced. The user library in its current form was too closed. This is not a critism of the GWT engineering effort, this happens to all frameworks and will happen to the Gems framework too\n# I didn't like the idea of a platform specific distribution in order to do a compile. Only the shell suffers from platform issues, the compiler is free of them\n# I didn't like bundling a fat user lib binary into my war/web application when all I needed was the rpc subsystem.\n\nRationale:\nIMHO the current physical architecture and distribution model raises a number of issues. If the following design principles where observed then perhaps these\n# The Reuse Release Equivalency Principle (REP) - The granule of reuse, is the granule of release. In my 2 months with GWT I have observed a highly stable GWT Compiler. Hats off to the engineering team. However, as one would expect the widget library has experienced some volatility. The widget library clearly needs a different release cycle than the compiler.\n# The Common Reuse Principle (CRP) - Thing that aren't used together, should not be grouped together. Remember, once the ~GWTCompiler has generated the javascript rendering of the client part of the application, the user lib and emulation subsystem serve no purpose.</div>
<div tiddler="WikifyContentsMacro" modifier="SimonBaird" modified="200603080754" created="200603080005" tags="systemConfig">//{{{\n// for use in templates\nconfig.macros.wikifyContents = {};\nconfig.macros.wikifyContents.handler = function (place,macroName,params,wikifier,paramString,tiddler) {\n var contents = place.innerHTML;\n // to avoid CSS complications change the xmp to a div...\n var newDiv = document.createElement(&quot;div&quot;);\n newDiv.className = place.className;\n newDiv.setAttribute(&quot;style&quot;,place.getAttribute(&quot;style&quot;));\n place.parentNode.insertBefore(newDiv,place);\n place.parentNode.removeChild(place);\n wikify(contents.trim().replace(/\s\s\sr\sn/mg,'').replace(/\s\s\sn/mg,''), newDiv, null, tiddler); // the replace is a hack for non-br-ing line breaks\n}\n//}}}\n</div>
		</div>










	</body>
</html>
